Bagaimana cara membatasi nilai float hanya ke dua tempat setelah titik desimal di C?


215

Bagaimana saya bisa membulatkan nilai float (seperti 37.777779) ke dua tempat desimal (37,78) di C?


15
Anda tidak dapat membulatkan angka itu dengan benar, karena float(dan double) bukan floating-point desimal - mereka adalah floating-point biner - jadi pembulatan ke posisi desimal tidak ada artinya. Anda dapat membulatkan output.
Pavel Minaev

63
Itu tidak berarti; itu tidak tepat. Ada perbedaan.
Brooks Moses

2
Pembulatan seperti apa yang Anda harapkan? Setengah atau Pembulatan ke terdekat bahkan?
Truthseeker Rangwan

Jawaban:


407

Jika Anda hanya ingin membulatkan angka untuk tujuan keluaran, maka "%.2f"string format memang jawaban yang benar. Namun, jika Anda benar-benar ingin membulatkan nilai floating point untuk perhitungan lebih lanjut, sesuatu seperti yang berikut berfungsi:

#include <math.h>

float val = 37.777779;

float rounded_down = floorf(val * 100) / 100;   /* Result: 37.77 */
float nearest = roundf(val * 100) / 100;  /* Result: 37.78 */
float rounded_up = ceilf(val * 100) / 100;      /* Result: 37.78 */

Perhatikan bahwa ada tiga aturan pembulatan berbeda yang mungkin ingin Anda pilih: pembulatan ke bawah (yaitu, potong setelah dua tempat desimal), dibulatkan ke terdekat, dan pembulatan ke atas. Biasanya, Anda ingin berkeliling ke terdekat.

Seperti yang beberapa orang lain tunjukkan, karena keanehan representasi floating point, nilai-nilai bulat ini mungkin bukan nilai desimal "jelas", tetapi mereka akan sangat dekat.

Untuk lebih banyak (banyak!) Informasi lebih lanjut tentang pembulatan, dan terutama tentang aturan untuk memutuskan untuk pembulatan ke terdekat, lihat artikel Wikipedia tentang Pembulatan .


4
Bisakah itu dimodifikasi untuk mendukung pembulatan ke presisi sewenang-wenang?

1
@ Slater Ketika Anda mengatakan 'presisi sewenang-wenang', apakah Anda bertanya tentang pembulatan ke, misalnya, tiga bukannya dua tempat desimal, atau menggunakan perpustakaan yang menerapkan nilai desimal presisi tanpa batas? Jika yang pertama, buat apa yang saya harap adalah penyesuaian yang jelas terhadap konstanta 100; jika tidak, lakukan perhitungan yang sama persis seperti yang ditunjukkan di atas, hanya dengan pustaka multi presisi apa pun yang Anda gunakan.
Dale Hagglund

2
@DaleHagglung Yang pertama, terima kasih. Apakah penyesuaian untuk mengganti 100 dengan pow (10, (int) diinginkanPrecision)?

3
Ya. Untuk membulatkan setelah k tempat desimal, gunakan faktor skala 10 ^ k. Ini seharusnya sangat mudah untuk dilihat jika Anda menulis beberapa nilai desimal dengan tangan dan bermain-main dengan kelipatan 10. Misalkan Anda bekerja dengan nilai 1.23456789, dan ingin membulatkannya ke 3 tempat desimal. Operasi yang tersedia untuk Anda bulat ke integer . Jadi, bagaimana Anda memindahkan tiga tempat desimal pertama sehingga mereka tertinggal dari titik desimal? Saya harap Anda mengalikan 10 ^ 3. Sekarang Anda bisa membulatkan nilai itu ke integer. Selanjutnya, Anda mengembalikan tiga digit urutan rendah dengan membaginya dengan 10 ^ 3.
Dale Hagglund

1
Bisakah saya membuat ini bekerja dengan doublesentah bagaimana? Sepertinya tidak melakukan pekerjaan yang saya inginkan :( (menggunakan floordan ceil).
Ms. Nobody

87

Menggunakan % .2f di printf. Hanya mencetak 2 titik desimal.

Contoh:

printf("%.2f", 37.777779);

Keluaran:

37.77

Cara ini lebih baik karena tidak ada kehilangan presisi.
albert

2
@Albert Ini juga memiliki keuntungan tanpa kehilangan floatjangkauan karena val * 100bisa meluap.
chux - Reinstate Monica

42

Dengan asumsi Anda berbicara tentang nilai untuk mencetak, maka jawaban Andrew Coleson dan AraK benar:

printf("%.2f", 37.777779);

Tetapi perhatikan bahwa jika Anda bermaksud membulatkan angka menjadi tepat 37,78 untuk penggunaan internal (misalnya untuk membandingkan dengan nilai lain), maka ini bukan ide yang baik, karena cara angka floating point bekerja: Anda biasanya tidak ingin melakukan perbandingan kesetaraan untuk floating point, alih-alih gunakan nilai target +/- nilai sigma. Atau menyandikan angka sebagai string dengan ketepatan yang diketahui, dan bandingkan itu.

Lihat tautan dalam jawaban Greg Hewgill untuk pertanyaan terkait , yang juga mencakup mengapa Anda tidak boleh menggunakan floating point untuk perhitungan keuangan.


1
Terpilih untuk menjawab apa yang mungkin menjadi pertanyaan di balik pertanyaan (atau pertanyaan yang seharusnya ada di balik pertanyaan!). Itu poin yang agak penting.
Brooks Moses

Sebenarnya 37,78 dapat disajikan persis dengan floating point. Float memiliki 11 hingga 12 digit untuk persiapan. Itu harus cukup untuk mengatasi 3778 377.8 atau semua jenis 4 digit desimal.
Anonim Putih

@HaryantoCiu ya cukup adil, saya sudah sedikit mengedit jawaban saya.
John Carter

presisi dinamis:printf("%.*f", (int)precision, (double)number);
Minhas Kamal

24

Bagaimana dengan ini:

float value = 37.777779;
float rounded = ((int)(value * 100 + .5) / 100.0);

4
-1: a) ini tidak akan berfungsi untuk angka negatif (ok, contohnya positif tapi tetap saja). b) Anda tidak menyebutkan bahwa tidak mungkin untuk menyimpan nilai desimal yang tepat di float
John Carter

32
@therefromhere: (a) Anda benar (b) Apa ini? Tes sekolah menengah?
Daniil

1
mengapa Anda menambahkan 0,5?
muhammad tayyab

1
Ini perlu untuk mengikuti aturan pembulatan.
Daniil

1
aturan pembulatan dalam konteks komentar @aniil bulat untuk yang terdekat
Shmil The Cat

20
printf("%.2f", 37.777779);

Jika Anda ingin menulis ke C-string:

char number[24]; // dummy size, you should take care of the size!
sprintf(number, "%.2f", 37.777779);

@ Sinan: Kenapa diedit? @AraK: Tidak, Anda harus mengurus ukurannya :). Gunakan snprintf ().
aib

1
@aib: Saya kira karena / ** / adalah komentar gaya C dan pertanyaannya ditandai untuk C
Michael Haren

5
C89 hanya diperbolehkan / ** / - style, C99 memperkenalkan dukungan untuk // - style. Gunakan compiler lame / old (atau mode paksa C89) dan Anda tidak akan dapat menggunakan gaya //. Karena itu, ini tahun 2009, mari kita pertimbangkan keduanya gaya C dan C ++.
Andrew Coleson

11

Tidak ada cara untuk membulatkan a floatke yang lain floatkarena yang dibulatkan floatmungkin tidak dapat diwakili (batasan angka floating-point). Sebagai contoh, katakanlah Anda membulatkan 37.777779 ke 37.78, tetapi angka representatif terdekat adalah 37.781.

Namun, Anda dapat "membulatkan" a floatdengan menggunakan fungsi format string.


3
Ini tidak berbeda dengan mengatakan "tidak ada cara untuk membagi dua pelampung dan mendapatkan pelampung, karena hasil yang dibagi mungkin tidak dapat diwakili," yang mungkin memang benar tetapi tidak relevan. Mengapung selalu tidak eksak, bahkan untuk sesuatu yang mendasar seperti penambahan; Asumsinya adalah selalu bahwa yang Anda dapatkan adalah "float yang paling mendekati perkiraan jawaban yang tepat".
Brooks Moses

Yang saya maksud adalah bahwa Anda tidak dapat membulatkan a floatke n tempat desimal dan kemudian mengharapkan hasilnya selalu memiliki n tempat desimal. Anda masih akan mendapatkan float, bukan yang Anda harapkan.
Andrew Keeton

9

Juga, jika Anda menggunakan C ++, Anda bisa membuat fungsi seperti ini:

string prd(const double x, const int decDigits) {
    stringstream ss;
    ss << fixed;
    ss.precision(decDigits); // set # places after decimal
    ss << x;
    return ss.str();
}

Anda kemudian dapat mengeluarkan ganda myDoubledengan ntempat setelah titik desimal dengan kode seperti ini:

std::cout << prd(myDouble,n);

7

Anda masih dapat menggunakan:

float ceilf(float x); // don't forget #include <math.h> and link with -lm.

contoh:

float valueToRound = 37.777779;
float roundedValue = ceilf(valueToRound * 100) / 100;

Ini memotong pada titik desimal (yaitu akan menghasilkan 37), dan ia perlu membulatkan ke dua tempat setelah titik desimal.
Pavel Minaev

Membulatkan ke dua tempat setelah titik desimal adalah variasi sepele, meskipun (tetapi masih harus disebutkan dalam jawaban; ZeroCool, ingin menambahkan suntingan?): Float roundedValue = ceilf (valueToRound * 100.0) / 100.0;
Brooks Moses

Ke keadaan tidur :)
ZeroCool

Kenapa solusi ini tidak lebih populer? Ini berfungsi persis seperti seharusnya dengan kode minimal. Apakah ada peringatan dengannya?
Andy

7

Di C ++ (atau dalam C dengan gips gaya-C), Anda bisa membuat fungsi:

/* Function to control # of decimal places to be output for x */
double showDecimals(const double& x, const int& numDecimals) {
    int y=x;
    double z=x-y;
    double m=pow(10,numDecimals);
    double q=z*m;
    double r=round(q);

    return static_cast<double>(y)+(1.0/m)*r;
}

Maka std::cout << showDecimals(37.777779,2);akan menghasilkan: 37,78.

Jelas Anda tidak benar-benar perlu membuat semua 5 variabel dalam fungsi itu, tetapi saya membiarkannya di sana sehingga Anda dapat melihat logikanya. Mungkin ada solusi yang lebih sederhana, tetapi ini bekerja dengan baik untuk saya - terutama karena ini memungkinkan saya untuk menyesuaikan jumlah digit setelah tempat desimal yang saya butuhkan.


5

Selalu gunakan printfkeluarga fungsi untuk ini. Bahkan jika Anda ingin mendapatkan nilai sebagai float, Anda sebaiknya menggunakan snprintfuntuk mendapatkan nilai bulat sebagai string dan kemudian menguraikannya kembali dengan atof:

#include <math.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>

double dround(double val, int dp) {
    int charsNeeded = 1 + snprintf(NULL, 0, "%.*f", dp, val);
    char *buffer = malloc(charsNeeded);
    snprintf(buffer, charsNeeded, "%.*f", dp, val);
    double result = atof(buffer);
    free(buffer);
    return result;
}

Saya mengatakan ini karena pendekatan yang ditunjukkan oleh jawaban terpilih saat ini dan beberapa lainnya di sini - mengalikan dengan 100, membulatkan ke bilangan bulat terdekat, dan kemudian membaginya dengan 100 lagi - cacat dalam dua cara:

  • Untuk beberapa nilai, itu akan membulatkan ke arah yang salah karena perkalian dengan 100 mengubah angka desimal menentukan arah pembulatan dari 4 ke 5 atau sebaliknya, karena ketidaktepatan angka floating point
  • Untuk beberapa nilai, mengalikan dan kemudian membaginya dengan 100 tidak bolak-balik, yang berarti bahwa bahkan jika tidak ada pembulatan terjadi hasil akhirnya akan salah

Untuk menggambarkan jenis kesalahan pertama - arah pembulatan terkadang salah - coba jalankan program ini:

int main(void) {
    // This number is EXACTLY representable as a double
    double x = 0.01499999999999999944488848768742172978818416595458984375;

    printf("x: %.50f\n", x);

    double res1 = dround(x, 2);
    double res2 = round(100 * x) / 100;

    printf("Rounded with snprintf: %.50f\n", res1);
    printf("Rounded with round, then divided: %.50f\n", res2);
}

Anda akan melihat output ini:

x: 0.01499999999999999944488848768742172978818416595459
Rounded with snprintf: 0.01000000000000000020816681711721685132943093776703
Rounded with round, then divided: 0.02000000000000000041633363423443370265886187553406

Perhatikan bahwa nilai yang kami mulai dengan kurang dari 0,015, sehingga jawaban matematis yang benar ketika membulatkannya ke 2 tempat desimal adalah 0,01. Tentu saja, 0,01 tidak persis representable sebagai ganda, tapi kami berharap hasil kami untuk menjadi yang terdekat ganda untuk 0,01. Menggunakan snprintfmemberi kita hasil itu, tetapi menggunakan round(100 * x) / 100memberi kita 0,02, yang salah. Mengapa? Karena 100 * xmemberi kita persis 1,5 sebagai hasilnya. Mengalikan dengan 100 dengan demikian mengubah arah yang benar untuk dibulatkan.

Untuk mengilustrasikan jenis kesalahan kedua - hasilnya kadang-kadang salah karena * 100dan / 100tidak benar-benar menjadi terbalik satu sama lain - kita dapat melakukan latihan serupa dengan jumlah yang sangat besar:

int main(void) {
    double x = 8631192423766613.0;

    printf("x: %.1f\n", x);

    double res1 = dround(x, 2);
    double res2 = round(100 * x) / 100;

    printf("Rounded with snprintf: %.1f\n", res1);
    printf("Rounded with round, then divided: %.1f\n", res2);
}

Nomor kami sekarang bahkan tidak memiliki bagian pecahan; ini adalah nilai integer, hanya disimpan dengan tipe double. Jadi hasilnya setelah membulatkannya harus angka yang sama dengan yang kita mulai, kan?

Jika Anda menjalankan program di atas, Anda akan melihat:

x: 8631192423766613.0
Rounded with snprintf: 8631192423766613.0
Rounded with round, then divided: 8631192423766612.0

Ups. snprintfMetode kami mengembalikan hasil yang benar lagi, tetapi pendekatan multiply-kemudian-round-kemudian-membagi gagal. Itu karena nilai matematis yang benar dari 8631192423766613.0 * 100,, 863119242376661300.0tidak dapat direpresentasikan secara ganda; nilai terdekatnya adalah 863119242376661248.0. Ketika Anda membagi kembali dengan 100, Anda mendapatkan 8631192423766612.0- nomor yang berbeda dengan yang Anda mulai.

Mudah-mudahan itu demonstrasi yang cukup bahwa menggunakan roundfuntuk pembulatan ke sejumlah tempat desimal rusak, dan yang harus Anda gunakan snprintfsebagai gantinya. Jika itu terasa seperti retasan yang mengerikan bagi Anda, mungkin Anda akan diyakinkan oleh pengetahuan bahwa pada dasarnya itulah yang dilakukan CPython .


Memberi +1 untuk contoh konkret tentang apa yang salah dengan jawaban saya dan yang serupa dengan itu, berkat keanehan IEEE floating point, dan memberikan alternatif langsung. Saya sadar secara periferal, cukup lama, banyak upaya yang dilakukan untuk mencetak dan teman-teman saya mereka aman untuk nilai-nilai floating point trip-tripping. Saya kira pekerjaan yang dilakukan mungkin akan muncul di sini.
Dale Hagglund

Ahem ... Maaf untuk kata salad di dekat akhir sana, yang sekarang sudah terlambat untuk mengedit. Apa yang ingin saya katakan adalah "... banyak upaya dimasukkan ke dalam printf dan teman-teman untuk membuat mereka aman ..."
Dale Hagglund

4

Menggunakan float roundf(float x) .

"Putaran fungsi bulat argumen mereka ke nilai integer terdekat dalam format floating-point, pembulatan setengah kasus jauh dari nol, terlepas dari arah pembulatan saat ini." C11dr §7.12.9.5

#include <math.h>
float y = roundf(x * 100.0f) / 100.0f; 

Bergantung pada floatimplementasi Anda , angka yang tampaknya setengah jalan bukan. sebagai floating-point biasanya berbasis-2. Lebih lanjut, pembulatan yang tepat ke 0.01kasus-kasus "setengah jalan" terdekat adalah yang paling menantang.

void r100(const char *s) {
  float x, y;
  sscanf(s, "%f", &x);
  y = round(x*100.0)/100.0;
  printf("%6s %.12e %.12e\n", s, x, y);
}

int main(void) {
  r100("1.115");
  r100("1.125");
  r100("1.135");
  return 0;
}

 1.115 1.115000009537e+00 1.120000004768e+00  
 1.125 1.125000000000e+00 1.129999995232e+00
 1.135 1.134999990463e+00 1.139999985695e+00

Meskipun "1,115" adalah "setengah jalan" antara 1,11 dan 1,12, ketika dikonversi menjadi float, nilainya adalah 1.115000009537...dan tidak lagi "setengah jalan", tetapi lebih dekat ke 1,12 dan berputar ke titik terdekat floatdari1.120000004768...

"1,125" adalah "setengah jalan" antara 1,12 dan 1,13, ketika dikonversi menjadi float, nilainya persis1.125 dan "setengah jalan". Hal putaran ke 1,13 karena hubungan dengan aturan bahkan dan putaran ke terdekat floatdari1.129999995232...

Meskipun "1,135" adalah "setengah jalan" antara 1,13 dan 1,14, ketika dikonversi menjadi float, nilainya adalah 1.134999990463...dan tidak lagi "setengah jalan", tetapi lebih dekat ke 1,13 dan berputar ke terdekatfloat dari1.129999995232...

Jika kode digunakan

y = roundf(x*100.0f)/100.0f;

Meskipun "1,135" adalah "setengah jalan" antara 1,13 dan 1,14, bila dikonversi ke float, nilai 1.134999990463...dan tidak lagi "setengah jalan", tetapi lebih dekat dengan 1,13 tetapi tidak benar putaran ke floatdari 1.139999985695...karena lebih presisi terbatas floatvs double. Nilai yang salah ini dapat dianggap benar, tergantung pada tujuan pengkodean.


4

Saya membuat makro ini untuk pembulatan angka float. Tambahkan di header / file Anda

#define ROUNDF(f, c) (((float)((int)((f) * (c))) / (c)))

Berikut ini sebuah contoh:

float x = ROUNDF(3.141592, 100)

x sama dengan 3,14 :)


Ini terpotong, tetapi pertanyaan meminta pembulatan. Selain itu, itu dapat menyebabkan kesalahan pembulatan dalam operasi floating-point.
Eric Postpischil

3
double f_round(double dval, int n)
{
    char l_fmtp[32], l_buf[64];
    char *p_str;
    sprintf (l_fmtp, "%%.%df", n);
    if (dval>=0)
            sprintf (l_buf, l_fmtp, dval);
    else
            sprintf (l_buf, l_fmtp, dval);
    return ((double)strtod(l_buf, &p_str));

}

Ini nadalah angka desimal

contoh:

double d = 100.23456;

printf("%f", f_round(d, 4));// result: 100.2346

printf("%f", f_round(d, 2));// result: 100.23

-1 untuk empat alasan: 1) kurangnya penjelasan, 2) kerentanan terhadap buffer overflow - ini akan meluap, dan karena itu sangat mungkin crash, jika dvalsangat besar 3) aneh if/ elseblok tempat Anda melakukan hal yang persis sama di setiap cabang , dan 4) penggunaan terlalu rumit sprintfuntuk membangun penentu format untuk sprintfpanggilan kedua ; lebih mudah untuk hanya menggunakan .*dan meneruskan nilai ganda dan jumlah tempat desimal sebagai argumen untuk sprintfpanggilan yang sama .
Mark Amery

3

Definisi kode:

#define roundz(x,d) ((floor(((x)*pow(10,d))+.5))/pow(10,d))

Hasil:

a = 8.000000
sqrt(a) = r = 2.828427
roundz(r,2) = 2.830000
roundz(r,3) = 2.828000
roundz(r,5) = 2.828430

0

Ijinkan saya mencoba untuk membenarkan alasan saya untuk menambahkan jawaban lain untuk pertanyaan ini. Dalam dunia yang ideal, pembulatan sebenarnya bukan masalah besar. Namun, dalam sistem nyata, Anda mungkin perlu menghadapi beberapa masalah yang dapat mengakibatkan pembulatan yang mungkin tidak seperti yang Anda harapkan. Misalnya, Anda dapat melakukan perhitungan keuangan di mana hasil akhir dibulatkan dan ditampilkan kepada pengguna sebagai 2 tempat desimal; nilai-nilai yang sama disimpan dengan presisi tetap dalam database yang dapat mencakup lebih dari 2 tempat desimal (karena berbagai alasan; tidak ada jumlah tempat yang optimal untuk disimpan ... tergantung pada situasi spesifik yang harus didukung setiap sistem, misalnya barang kecil yang harganya adalah pecahan satu sen per unit); dan, perhitungan floating point dilakukan pada nilai-nilai di mana hasilnya plus / minus epsilon. Saya telah menghadapi masalah ini dan mengembangkan strategi saya sendiri selama bertahun-tahun. Saya tidak akan mengklaim bahwa saya telah menghadapi setiap skenario atau memiliki jawaban terbaik, tetapi di bawah ini adalah contoh pendekatan saya sejauh ini yang mengatasi masalah ini:

Misalkan 6 tempat desimal dianggap sebagai presisi yang cukup untuk perhitungan float / doubles (keputusan arbitrer untuk aplikasi spesifik), menggunakan fungsi / metode pembulatan berikut:

double Round(double x, int p)
{
    if (x != 0.0) {
        return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
    } else {
        return 0.0;
    }
}

Pembulatan ke 2 tempat desimal untuk presentasi hasil dapat dilakukan sebagai:

double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));

Sebab val = 6.825, hasilnya 6.83seperti yang diharapkan.

Sebab val = 6.824999, hasilnya adalah 6.82. Di sini asumsinya adalah bahwa perhitungan menghasilkan tepat 6.824999dan tempat desimal ke-7 adalah nol.

Sebab val = 6.8249999, hasilnya adalah 6.83. Tempat desimal ke-7 yang berada 9dalam kasus ini menyebabkan Round(val,6)fungsi memberikan hasil yang diharapkan. Untuk kasus ini, mungkin ada sejumlah jejak 9.

Sebab val = 6.824999499999, hasilnya adalah 6.83. Membulatkan ke tempat desimal ke-8 sebagai langkah pertama, yaitu Round(val,8)menangani satu kasus yang tidak menyenangkan di mana hasil floating point dihitung 6.8249995, tetapi secara internal direpresentasikan sebagai6.824999499999... .

Akhirnya, contoh dari pertanyaan ... val = 37.777779menghasilkan37.78 .

Pendekatan ini selanjutnya dapat digeneralisasi sebagai:

double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));

di mana N adalah presisi yang harus dipertahankan untuk semua perhitungan menengah pada mengapung / ganda. Ini bekerja pada nilai negatif juga. Saya tidak tahu apakah pendekatan ini benar secara matematis untuk semua kemungkinan.


0

Kode C sederhana untuk membulatkan angka:

float n = 3.56;
printf("%.f", n);

Ini akan Output:

4

-1

... atau Anda dapat melakukannya dengan cara lama tanpa pustaka:

float a = 37.777779;

int b = a; // b = 37    
float c = a - b; // c = 0.777779   
c *= 100; // c = 77.777863   
int d = c; // d = 77;    
a = b + d / (float)100; // a = 37.770000;

Itu tentu saja jika Anda ingin menghapus informasi tambahan dari nomor tersebut.


-2

fungsi ini mengambil angka dan presisi dan mengembalikan angka yang dibulatkan

float roundoff(float num,int precision)
{
      int temp=(int )(num*pow(10,precision));
      int num1=num*pow(10,precision+1);
      temp*=10;
      temp+=5;
      if(num1>=temp)
              num1+=10;
      num1/=10;
      num1*=10;
      num=num1/pow(10,precision+1);
      return num;
}

itu mengubah angka floating point ke int dengan menggeser titik dan memeriksa untuk lebih dari lima kondisi.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.