En fazla 2 ondalık basamağa yuvarlayın (yalnızca gerekirse)


2757

En fazla 2 ondalık haneyi yuvarlamak istiyorum, ancak sadece gerekirse .

Giriş:

10
1.7777777
9.1

Çıktı:

10
1.78
9.1

Bunu JavaScript'te nasıl yapabilirim?


22
Burada çözüm olarak sunulan birçok teknikle bir keman yaptım ... böylece karşılaştırabilirsiniz: fiddle
dsdsdsdsd

37
Kimse farkında değil gibi görünüyor Number.EPSILON. Kullanın Math.round( num * 100 + Number.EPSILON ) / 100.
17'de cronvel

3
Yeni okuyucular için, bir dize türü sonucunuz yoksa bunu yapamazsınız . Sayıların ikili iç temsillerinde kayan nokta matematiği, her zaman düzgün ondalık sayı olarak temsil edilemeyen sayılar olduğu anlamına gelir .
Walf

9
@cronvel Number.EPSILONBurada kullanmanın nedenini açıklayabilir misiniz ?
Bruce Sun

5
Tavşan deliğinden düştüm ve bu sayfadaki bazı ilginç cevapları test ettim (sadece ilk sayfa). İşte bir Codepen . İpucu: Cevabınız ne kadar fazla oy verirse, düzgün çalışma şansı o kadar düşük olur.
Adam Jagosz

Yanıtlar:


3492

kullanım Math.round(num * 100) / 100

Düzenleme: 1.005 gibi şeyleri doğru bir şekilde sağlamak için kullanırız

Math.round((num + Number.EPSILON) * 100) / 100


395
Bu çoğu durumda işe yarayacak olsa da, 1.001 için çalışmayacak ve sonuçta 1.01 yerine 1 olacak
James

83
@ James Wow bu gerçekten garip- Chrome geliştirici konsolunda çalışıyorum ve 1.005 * 100 = 100.49999999999999 olduğunu fark ediyorum. Math.round (100.49999999999999) 100 olarak değerlendirilirken, Math.round (100.5) 101 olarak değerlendirilir. IE9 da aynı şeyi yapar. Bu Javascript kayan nokta gariplik
stinkycheeseman

153
Basit bir çözüm. 2 dp için kullanın Math.round((num + 0.00001) * 100) / 100. Deneyin Math.round((1.005 + 0.00001) * 100) / 100veMath.round((1.0049 + 0.00001) * 100) / 100
mrkschan

31
@mrkschan Bu neden çalışıyor ve bu tüm sayılar için kusursuz mu?
CMCDragonkai

86
Anlamayanlarınız için bu tekniğe ölçekleme denir. Temel olarak burada cevap, ondalık noktadan iki rakamı getirerek, tüm çılgın kayan nokta sorunlarından kaçınmak için rakamı bir tam sayıya çevirmek, onu yuvarlamak ve daha sonra 100'e bölerek daha önce olduğu haline geri çevirmek ve 2dp'ye cevap.
Alex_Nabu

3063

Değer bir metin türüyse:

parseFloat("123.456").toFixed(2);

Değer bir sayı ise:

var numb = 123.23454;
numb = numb.toFixed(2);

1.5 gibi değerlerin çıktı olarak "1.50" vereceği bir dezavantaj vardır. @Minitech tarafından önerilen bir düzeltme:

var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.

Daha Math.roundiyi bir çözüm gibi görünüyor . Ama öyle değil! Bazı durumlarda olacak DEĞİL yuvarlak düzgün:

Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!

toFixed () de DEĞİLDİR yuvarlak doğru bazı durumlarda (Krom v.55.0.2883.87 test)!

Örnekler:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.

1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.

Sanırım, bunun nedeni 1.555'in aslında sahne arkasında yüzer 1.55499994 gibi bir şey olması.

Çözüm 1 , gerekli yuvarlama algoritmasına sahip bir komut dosyası kullanmaktır, örneğin:

function roundNumber(num, scale) {
  if(!("" + num).includes("e")) {
    return +(Math.round(num + "e+" + scale)  + "e-" + scale);
  } else {
    var arr = ("" + num).split("e");
    var sig = ""
    if(+arr[1] + scale > 0) {
      sig = "+";
    }
    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
  }
}

https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

NOT: Bu herkes için evrensel bir çözüm değildir. Birkaç farklı yuvarlama algoritması vardır, uygulamanız farklı olabilir, gereksinimlerinize bağlıdır. https://en.wikipedia.org/wiki/Rounding

Çözüm 2 , ön uç hesaplamalarından kaçınmak ve arka uç sunucusundan yuvarlanmış değerleri almaktır.


81
Bu bir (toFixed) yaklaşımı iyi ve benim için çalıştı, ama özellikle gelmez değil "sadece gerektiğinde" orijinal talebi yerine. (Spesifikasyonu bozan 1.5 ila 1.50 arasında yuvarlar.)
Per Lundberg

29
"Gerektiğinde" gereklilik için şunu yapın: parseFloat(number.toFixed(decimalPlaces)); @PerLundberg
Onur Yıldırım

36
parseFloat("55.555").toFixed(2)"55.55"Chrome geliştirme konsolunda döner .
Levi Botelho

22
Math.round yerine toFixed kullanmanın bir avantajı yoktur; toFixed, aynı yuvarlama sorunlarına yol açar (5.555 ve 1.005 ile deneyin), ancak Math.round'dan 500x (şaka yapmaz) gibi yavaştır ... Görünüşe göre @MarkG cevabı burada daha doğru.
Pierre

17
toFixed "bazen" bir dize döndürmez, her zaman bir dize döndürür.
McGuireV10

464

Kullanabilirsiniz

function roundToTwo(num) {    
    return +(Math.round(num + "e+2")  + "e-2");
}

Bunu MDN'de buldum . Onların yolu edildi 1.005 ile sorunu önler sözü .

roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57

13
@Redsandro, +(val)kullanmaya zorlamanın eşdeğeridir Number(val). "E-2" nin bir sayı ile birleştirilmesi, bir sayıya geri dönüştürülmesi gereken bir dizeyle sonuçlandı.
Jack

41
Örneğin, "+ 1e-21 + 2" nin düzgün ayrıştırılmayacağı için NaN üretecek büyük ve küçük şamandıralar için dikkatli olun.
Pierre

16
1.005 'sorununu' çözdünüz ', ancak yeni bir sorun yarattınız: şimdi, Chrome konsolunda roundToTwo(1.0049999999999999)1,01 olarak çıkıyor (kaçınılmaz olarak, o zamandan beri 1.0049999999999999 == 1.005). Bana öyle geliyor ki, num = 1.005'açık bir şekilde' yazdığınızda elde ettiğiniz şamandıra 1.00'a yuvarlanmalıdır, çünkü num'un kesin değeri 1.005'ten azdır. Tabii ki, bana göre '1.005' 'açıkça' 'dizgesi 1.01'e yuvarlanmalıdır. Farklı insanların burada gerçek doğru davranışın ne olduğu konusunda farklı sezgilere sahip oldukları gerçeği, neden karmaşık olduğunun bir parçasıdır.
Mark Amery

35
Aralarında (kayan nokta) bir sayı yoktur 1.0049999999999999ve 1.005tanım gereği aynı sayıdır. Buna dedekind kesimi denir.
Azmisov

6
@Azmisov haklı. Olduğu 1.00499 < 1.005zaman true, olarak 1.0049999999999999 < 1.005değerlendirir false.
falconepl

146

MarkG'nin cevabı doğru. İşte herhangi bir ondalık basamak için genel bir uzantı.

Number.prototype.round = function(places) {
  return +(Math.round(this + "e+" + places)  + "e-" + places);
}

Kullanımı:

var n = 1.7777;    
n.round(2); // 1.78

Ünite testi:

it.only('should round floats to 2 places', function() {

  var cases = [
    { n: 10,      e: 10,    p:2 },
    { n: 1.7777,  e: 1.78,  p:2 },
    { n: 1.005,   e: 1.01,  p:2 },
    { n: 1.005,   e: 1,     p:0 },
    { n: 1.77777, e: 1.8,   p:1 }
  ]

  cases.forEach(function(testCase) {
    var r = testCase.n.round(testCase.p);
    assert.equal(r, testCase.e, 'didn\'t get right number');
  });
})

20
Pierre, MarkG'nin cevabı ile ciddi bir sorun yarattı.
dsjoerg

9
Not: Number.prototype'i değiştirmek istemiyorsanız - bunu bir işlev olarak yazın:function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
Philipp Tsipman

2
Bunu genişletmenin harika bir yolu. Ondalık negatif olup olmadığını kontrol edebilir, sonra e + ve e- 'yi ters çevirebilirsiniz. Sonra n = 115; n.round (2); 100 üretecek
Lee Louviere

3
Deneyin n: 1e + 19 - NaN döndürür
DavidJ

3
Bu algoritma her zaman yukarı çıkar (sıfırdan uzak yerine). Yani, negatif sayılar durumunda, çıktı beklediğiniz gibi değildir:(-1.005).round(2) === -1
Aleksej Komarov

123

Kullanmalısın:

Math.round( num * 100 + Number.EPSILON ) / 100

Kimse farkında değil gibi görünüyor Number.EPSILON.

Ayrıca, bunun bazı insanların belirttiği gibi bir JavaScript tuhaflığı olmadığını belirtmek gerekir.

Kayan nokta sayılarının bir bilgisayarda çalışması budur. Programlama dillerinin% 99'u gibi, JavaScript'in de evde kayan noktalı sayıları yoktur; bunun için CPU / FPU'ya dayanır. Bir bilgisayar ikili kullanır ve ikili dosyada,0.1 , sadece ikili bir yaklaşım vardır. Neden? Aynı nedenden ötürü 1/3 'ten ondalık olarak yazılamaz: değeri 0.33333333 ... üçü sonsuzdur.

İşte gel Number.EPSILON. Bu sayı, çift ​​kesinlikli kayar nokta sayılarında bulunan 1 ile sonraki sayı arasındaki farktır . Bu kadar: 1ve 1 + arasında bir sayı yoktur Number.EPSILON.

DÜZENLE:

Yorumlarda belirtildiği gibi, bir şeyi açıklığa kavuşturalım: ekleme Number.EPSILON , yalnızca yuvarlanan değer aritmetik bir işlemin sonucu olduğunda geçerlidir, çünkü bazı kayan nokta hatası deltalarını yutabilir.

Değer doğrudan bir kaynaktan geldiğinde yararlı değildir (örneğin: değişmez değer, kullanıcı girişi veya sensör).

DÜZENLE (2019):

@Maganap ve bazı insanların işaret ettiği gibi, çarpmadan Number.EPSILONönce eklemek en iyisidir :

Math.round( ( num + Number.EPSILON ) * 100 ) / 100

EDIT (Aralık 2019):

Son zamanlarda, epsilon bilinçli sayıları karşılaştırmak için buna benzer bir işlev kullanıyorum:

const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;

function epsilonEquals( a , b ) {
  if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
    return false ;
  }
  if ( a === 0 || b === 0 ) {
    return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
  }
  return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}

Benim kullanım durumum yıllardır geliştirdiğim bir iddia + veri doğrulama lib .

Aslında, kodda kullanıyorum ESPILON_RATE = 1 + 4 * Number.EPSILONve EPSILON_ZERO = 4 * Number.MIN_VALUE(dört kez epsilon), çünkü kayan nokta hatası birikimi için yeterince gevşek bir eşitlik denetleyicisi istiyorum.

Şimdiye kadar benim için mükemmel görünüyor. Umarım yardımcı olur.


1
@palota, EPSILON cronvel'in bahsettiği gibi gerçekten küçük bir sayı tanımlayabilirsiniz
Daniel San

3
Bu doğru cevap! Sorun, float sayılarının dahili olarak nasıl çalıştığıyla ilgilidir, javascript ile ilgili değildir
Daniel San

22
Aslında, 100 ile çarpmadan ÖNCE epsilon eklemelisiniz Math.round( (num + Number.EPSILON) * 100) / 100. Ayrıca, bu doğru şekilde yuvarlamak için doğru yöntem olduğunu kabul ediyorum (bu soruda tam olarak ne sorulmasına rağmen).
maganap

2
@cronvel Hmm. Haklısın; yön seçimi yapar mantıklı. Sanırım geri kalan itirazım, bunu yapmak sadece değerinizin "yuvarlak bir sayı" olduğunu düşünmek için temel bir nedeniniz olan bir alanda çalışıyorsanız mantıklı olmasıdır. Girişinizin az sayıda ondalık basamağa sahip sayılardaki basit aritmetik işlemlerin sonucu olduğunu biliyorsanız, muhtemelen sadece 0.004999999999999999bileşik kayan nokta hatasının sonucuna sahipsiniz ve matematiksel olarak doğru sonuç muhtemelen 0.005 idi. Bir sensörden bir okuma ise? Çok değil.
Mark Amery

1
@marchaos Başarısız değildir: yarıları hep yuvarlanır kadar . Ör. Math.round(1.5)= 2, ancak Math.round(-1.5)= -1. Yani bu tamamen tutarlı. Burada -1 -2'den büyüktür, tıpkı -1000 -1000.01'den büyüktür. Daha büyük mutlak sayılarla karıştırılmamalıdır .
cronvel

84

Biri kullanabilir .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23

4
Ayrıca , orijinal sorunun istediği gibi olmayan sıfırlar eklediğinden .
Alastair Maw

2
Ancak sondaki sıfırlar bir regex ile kolayca soyulur, yani `` Number (10.10000.toFixed (2) .replace (/ 0 + $ /, '')) `=> 10.1
Çad

1
@daniel üst cevabı (şimdi itibariyle) aynıdır ama gelmez alsways yuvarlak düzgün, denemek +(1.005).toFixed(2)hangi getiri 1yerine 1.01.
Emile Bergeron

3
@ChadMcElligott: Normal ifadeniz tamsayılarla iyi çalışmıyor: Number(9).toFixed(2).replace(/0+$/, '')=> "9."
Jacob van Lingen

Bu yuvarlak değil. Sadece temelde kesiliyor. Bazı durumlarda, yeterince iyi! Teşekkürler.
iedmrc

78

Bu soru karmaşık.

roundTo2DP(num)Bir şamandırayı bağımsız değişken olarak alan ve 2 ondalık basamağa yuvarlanmış bir değer döndüren bir fonksiyonumuz olduğunu varsayalım . Bu ifadelerin her biri neyi değerlendirmelidir?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

'Açık' cevap ilk örneğin 0,01'e yuvarlanmasıdır (çünkü 0,02'den 0,02'ye daha yakındır), diğer ikisi 0,02'ye yuvarlanmalıdır (çünkü 0,0150000000000000001 0,02'den 0,02'ye daha yakındır ve 0,015, ve bu sayıların yuvarlandığı matematiksel bir kural vardır).

Tahmin edebileceğiniz yakalamak, yani roundTo2DP muhtemelen olamaz kendisine geçirilen her üç sayı olduğu için, bu bariz cevap vermek uygulanacaktır aynı sayıda . IEEE 754 ikili kayan nokta sayıları (JavaScript tarafından kullanılan tür) tamsayı olmayan sayıların çoğunu tam olarak temsil edemez ve bu nedenle yukarıdaki üç sayısal değişmez değerin tümü yakındaki geçerli bir kayan nokta numarasına yuvarlanır. Bu sayı, olduğu gibi, tam olarak

0,01499999999999999944488848768742172978818416595458984375

0.01'e 0.02'den daha yakın olan.

Her üç sayının da tarayıcı konsolunuzda, Düğüm kabuğunda veya diğer JavaScript yorumlayıcılarında aynı olduğunu görebilirsiniz. Sadece karşılaştırın:

> 0.014999999999999999 === 0.0150000000000000001
true

Ben yazdığınızda m = 0.0150000000000000001, kesin değerim ben ile sonuna kadar daha yakın 0.01o kadar olandan 0.02. Ve yine de, eğer mbir String'e dönüştürürsem ...

> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015

... Ben, 0.015 olsun hangi gerektiği yuvarlak 0.02 ve belirgin olan değil daha önce bu sayıların tümü tam olduğunu için eşit olduğunu söyledi 56-nokta-yer numarası. Peki bu ne karanlık sihir?

Cevap, ECMAScript spesifikasyonunda, 7.1.12.1: Numara türüne uygulanan ToString bölümünde bulunabilir . Burada, bazı Number m değerlerini bir String'e dönüştürmek için kurallar belirlenmiştir. Anahtar kısım nokta 5 olup, içinde m değerleri String gösteriminde basamakları kullanılacak bir tamsayı s oluşturulur :

izin n , k , ve s tamsayıları öyle ki k ≥ 1, 10 k -1s <10 k için sayı değeri S * 10 N - k olan m ve k mümkün olduğu kadar küçük olduğu gibi. K'nin s'nin ondalık gösterimindeki basamak sayısı olduğunu, s'nin 10 ile bölünemediğini ve en küçük anlamlı s basamağının mutlaka bu kriterler tarafından belirlenmediğini unutmayın.

Burada kilit nokta, " k'nin mümkün olduğunca küçük olması" gerekliliğidir . Bu gereksinimin ne anlama geldiği, bir Sayı verildiğinde m, değerinin mümkün olan en az sayıyaString(m) sahip olması ve yine de bu gereksinimi karşılaması gereken bir gerekliliktir Number(String(m)) === m. Bunu zaten bildiğimiz için 0.015 === 0.0150000000000000001, neden String(0.0150000000000000001) === '0.015'doğru olması gerektiği açık .

Tabii ki, bu tartışmanın hiçbiri neyin geri dönmesi roundTo2DP(m) gerektiğini doğrudan cevaplamadı . Eğer m'bireyin tam değer 0,01499999999999999944488848768742172978818416595458984375, ama onun dize temsilidir' 0.015, o zaman ne olduğunu doğru matematiksel, pratik olarak, felsefi olarak, ya da her türlü - - cevap tekrar iki ondalık basamağa o yuvarlak zaman?

Bunun tek bir doğru cevabı yok. Kullanım durumunuza bağlıdır. Muhtemelen Dize temsiline saygı göstermek ve şu durumlarda yukarı doğru yuvarlamak istiyorsunuz:

  • Temsil edilen değer doğal olarak ayrıktır, örneğin dinarlar gibi 3 ondalık basamaklı bir para birimindeki para birimi miktarı. Bu durumda, gerçek 0,015 gibi bir sayı değeri olan 0.015 ve ikili kayan noktasında aldığını ,0149999999 ... temsil yuvarlama hatadır. (Tabii ki, çoğu, makul olarak, bu tür değerleri işlemek için bir ondalık kitaplık kullanmanız ve bunları asla ikili kayan nokta Sayıları olarak göstermemeniz gerektiğini iddia edecektir.)
  • Değer bir kullanıcı tarafından yazılmıştır. Bu durumda, yine, girilen ondalık sayı en yakın ikili kayan nokta gösteriminden daha 'doğru' olur.

Öte yandan, değeriniz doğal olarak sürekli bir ölçekte olduğunda (örneğin, bir sensörden bir okuma ise) ikili kayan nokta değerine saygı göstermek ve aşağı doğru yuvarlamak istersiniz.

Bu iki yaklaşım farklı kodlar gerektirir. Sayının Dize temsiline saygı göstermek için (oldukça makul bir şekilde küçük bir kodla), okulda kullandığınız algoritmayı kullanarak doğrudan Dize temsiline göre basamak basamak basamak üzerinde hareket eden kendi yuvarlamamızı uygulayabiliriz sayıların nasıl yuvarlanacağı öğretildi. Aşağıda OP'nin sayıyı ondalık noktadan sonraki sıfırları sıyırarak "yalnızca gerektiğinde" 2 ondalık basamağa gösterme gereksinimini karşılayan bir örnek; elbette kesin ihtiyaçlarınıza göre ayarlamanız gerekebilir.

/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see http://stackoverflow.com/a/38676273/1709587
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        finalNumber = Number(beforePoint)+1;
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

Örnek kullanım:

> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'

Yukarıdaki işlev, kullanıcıların girdikleri sayıların yanlış yuvarlandığına tanık olmalarını önlemek için muhtemelen kullanmak istediğiniz işlevdir .

(Alternatif olarak, çılgınca farklı bir uygulama ile benzer şekilde davranan bir işlev sağlayan round10 kütüphanesini de deneyebilirsiniz .)

Peki ya ikinci tür bir Sayınız varsa - daha az ondalık basamağı olan yaklaşık ondalık temsillerin daha fazla olanlardan daha doğru olduğunu düşünmek için hiçbir nedenin olmadığı sürekli bir ölçekte alınan bir değer varsa ? Bu durumda, biz yok bu temsili (spec açıklandığı gibi) zaten, çünkü dize temsilini saygı istiyoruz sort-of-yuvarlak; "0,014999999 ... 0,05'e kadar 375 mermi, 0,02'ye kadar yuvarlama, yani 0,014999999 ... 375 mermi 0,02'ye kadar" diyerek hata yapmak istemiyoruz.

Burada yerleşik toFixedyöntemi kullanabiliriz. Tarafından Number()döndürülen Dize'yi çağırarak toFixed, Dize temsilinin sonunda sıfır olmayan bir Sayı elde ettiğimizi unutmayın (JavaScript, bu cevabın başlarında tartışılan bir Sayının Dize gösterimini hesaplama yöntemi sayesinde).

/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 * 
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}

Bu bazı durumlarda işe yaramaz: ile deneyin ( jsfiddle ) roundStringNumberWithoutTrailingZeroes(362.42499999999995, 2). (PHP gibi bir sonuç Beklenen echo round(362.42499999999995, 2)): 362.43. Gerçek sonuç:362.42
Dr.Glenluigi Zane Zanettini

1
@ Dr.GianluigiZaneZanettini Huh. Tuhaf. PHP'nin neden round362.43 verdiğinden emin değilim . Bu sezgisel olarak yanlış görünüyor, çünkü 362.42499999999995 362.425'ten azdır (matematik ve kodda - 362.42499999999995 < 362.425hem JS hem de PHP'de doğrudur). PHP'nin yanıtı, orijinal ve yuvarlak kayan nokta sayıları arasındaki mesafeyi de en aza indirmez 362.43 - 362.42499999999995 > 362.42499999999995 - 362.42. Php.net/manual/en/function.round.php'ye göre , PHP roundC99 standardını takip eder; Neler olduğunu anlamak için C diyarına girmem gerekecek.
Mark Amery

1
PHP'nin kaynağında yuvarlama uygulamasına baktıktan sonra ne yaptığı hakkında hiçbir fikrim yok. Makrolar ve dallar ve dize dönüşümleri ve sabit kodlanmış sihirli hassasiyet değerlerinde dallanmalarla dolu korkunç karmaşık bir uygulamadır. "PHP'nin cevabı açıkça yanlış ve biz bir hata raporu dosya gerekir" ötesinde ne söyleneceğinden emin değilim. Bu arada 362.42499999999995 sayısını nasıl buldun?
Mark Amery

1
@ Dr.GianluigiZaneZanettini Bir hata raporu oluşturdum: bugs.php.net/bug.php?id=75644
Mark Amery

2
@ Dr.GianluigiZaneZanettini "Herhangi bir anlam ifade ediyor muyum?" - Hayır; Yuvarlama bu şekilde çalışmaz. 1.000005 beşle biter, ancak en yakın tam sayıya yuvarlanırsa cevap 1 değil 2 olmalıdır. Benzer şekilde 1499995 beşle biter, ancak en yakın milyona yuvarlanırsa sonuç 2000000 değil 1000000 olmalıdır. 362.42499999999995 durumunda 2 DP'ye yuvarlanırsa, yuvarlama yönünü belirlemesi gereken üçüncü ondalık basamaktır, bu da 4'tür.
Mark Amery

76

Düşünün .toFixed()ve .toPrecision():

http://www.javascriptkit.com/javatutors/formatnumber.shtml


1
toFixed, ne olursa olsun her değere ondalık basamak ekler.
stinkycheeseman

13
Her ikisi de burada işe yaramaz
Esailija

Ne yazık ki, her iki işlev de @stinkycheeseman'ın istemediği ekstra ondalık basamaklar ekleyecektir.
jackwanders


2
sayıları değil, dizeleri döndürürler, bu yüzden biçimlendiriyorlar ve hesaplamıyorlar ..
saimiris_devel

63

Hassas bir yuvarlama yöntemi. Kaynak: Mozilla

(function(){

    /**
     * Decimal adjustment of a number.
     *
     * @param   {String}    type    The type of adjustment.
     * @param   {Number}    value   The number.
     * @param   {Integer}   exp     The exponent (the 10 logarithm of the adjustment base).
     * @returns {Number}            The adjusted value.
     */
    function decimalAdjust(type, value, exp) {
        // If the exp is undefined or zero...
        if (typeof exp === 'undefined' || +exp === 0) {
            return Math[type](value);
        }
        value = +value;
        exp = +exp;
        // If the value is not a number or the exp is not an integer...
        if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
            return NaN;
        }
        // Shift
        value = value.toString().split('e');
        value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
        // Shift back
        value = value.toString().split('e');
        return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
    }

    // Decimal round
    if (!Math.round10) {
        Math.round10 = function(value, exp) {
            return decimalAdjust('round', value, exp);
        };
    }
    // Decimal floor
    if (!Math.floor10) {
        Math.floor10 = function(value, exp) {
            return decimalAdjust('floor', value, exp);
        };
    }
    // Decimal ceil
    if (!Math.ceil10) {
        Math.ceil10 = function(value, exp) {
            return decimalAdjust('ceil', value, exp);
        };
    }
})();

Örnekler:

// Round
Math.round10(55.55, -1); // 55.6
Math.round10(55.549, -1); // 55.5
Math.round10(55, 1); // 60
Math.round10(54.9, 1); // 50
Math.round10(-55.55, -1); // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1); // -50
Math.round10(-55.1, 1); // -60
Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1); // 55.5
Math.floor10(59, 1); // 50
Math.floor10(-55.51, -1); // -55.6
Math.floor10(-51, 1); // -60
// Ceil
Math.ceil10(55.51, -1); // 55.6
Math.ceil10(51, 1); // 60
Math.ceil10(-55.59, -1); // -55.5
Math.ceil10(-59, 1); // -50

Birisi bunu GitHub ve npm'ye koydu: github.com/jhohlfeld/round10
Jo Liss

Hayır, Math.round10(3544.5249, -2)3544.52 yerine 3544.53
Matija

2
@Matija Wolfram alpha da 3544.52 diyor. Geçerli sayı ile yuvarlatılmış yaklaşık değer arasındaki hatayı en aza indirmek istiyorsunuz. 3544.52492 ondalık basamağa en yakın yaklaşım 3544.52(hata = 0.0049). Eğer öyleyse, 3544.53hata 0.0051 olacaktır. Art arda yuvarlama yapıyorsunuz, yani Math.round10 (Math.round10 (3544.5249, -3), -2), daha büyük bir yuvarlama hatası veriyor ve bu nedenle istenmiyor.
user

3
@Matija, matematiksel açıdan 3544.5249 yuvarlatılmış 3544.52, 3544.53 değil, bu nedenle bu kod doğrudur. Bu ve bunun gibi durumlarda 3544.53'e yuvarlatılmasını istiyorsanız (hatta zor olsa bile), böyle bir şey yapın:number += 0.00011
Bozidar Sikanjic

@Matija: Bence fonksiyon olması gerektiği gibi çalışıyor. Belki de yuvarlamayı tekrarlamak istersiniz:Math.round10( Math.round10(3544.5249, -3) , -2)
jumxozizi

60

Burada bulunan cevapların hiçbiri doğru değil . @stinkycheeseman istedi kadar yuvarlak tüm numarayı yuvarlak.

Yuvarlamak için şunu kullanın:

Math.ceil(num * 100)/100;

15
Örnek girdi ve çıktı, soru 'topla ...' demesine rağmen, aslında 'topla ...' olarak tasarlandığını göstermektedir.
JayDM

2
@ stinkycheeseman belirli bir durumda bir hatayı işaret ediyordu, tavanda olduğu gibi her zaman yuvarlamak istemiyordu, sadece 0.005'in
0.01'e yuvarlanması

9
Test ederken garip hata buldum Math.ceil(1.1 * 100)/100;-çok getiri 1.111.1 * 100 olduğu için, 110.00000000000001markasına yeni modern tarayıcılar Firefox, Chrome, Safari ve Opera ... IE, eski moda, hâlâ düşünür göre 1.1*100=1100.
skobaljic

1
@skobaljic tryMath.ceil(num.toFixed(4) * 100) / 100
treeface

1
@treeface Math.ceil((1.1).toFixed(4) * 100) / 100de 1.11Firefox'ta dönecek , modern tarayıcıların problemi / hatası çarpma ile ve insanlar bunu bilmeli (örneğin o zaman bir piyango oyunu üzerinde çalıştım).
skobaljic

47

İşte bunu yapmanın basit bir yolu:

Math.round(value * 100) / 100

Yine de sizin için bunu yapmak için ayrı bir işlev yapmak isteyebilirsiniz:

function roundToTwo(value) {
    return(Math.round(value * 100) / 100);
}

O zaman değeri geçeceksin.

İkinci bir parametre ekleyerek herhangi bir sayıda ondalık sayıya yuvarlamak için geliştirebilirsiniz.

function myRound(value, places) {
    var multiplier = Math.pow(10, places);

    return (Math.round(value * multiplier) / multiplier);
}

2
Bu çözüm yanlış, bkz. Stackoverflow.com/questions/38322372/… 156893.145 girerseniz ve yukarıdaki işlevle yuvarlarsanız, 156893.15 yerine 156893.14 alırsınız !!!
saimiris_devel

2
@saimiris_devel, JavaScript'teki sayıların sayısal sunumunun başarısız olduğu bir örnek bulmayı başardınız. Yanlış yuvarladığınızdan emin olabilirsiniz. Ancak, bunun nedeni 100 ile çarpıldığında örnek numaranızın zaten kırılmış olmasıdır (15689314.499999998). Gerçekten, tam özellikli bir cevap, JavaScript'in gerçek sayıları işlemedeki tutarsızlıkları hesaba katmak için özel olarak geliştirilmiş bir kütüphane gerektirir. Aksi takdirde, muhtemelen bu soruya verilen cevaplardan herhangi birini geçersiz kılabilirsiniz.
JayDM

36
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12

1
Bu, Numaranızın Dize temsilini alıp yuvarlarsanız alacağınız her zaman aynı sonuçları vermez. Örneğin +(0.015).toFixed(2) == 0.01,.
Mark Amery

35

Benim için Math.round () doğru cevap vermiyordu. Ben Fixed (2) daha iyi çalışır bulundu . Aşağıda her ikisinin de örnekleri bulunmaktadır:

console.log(Math.round(43000 / 80000) * 100); // wrong answer

console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer


ToFixed'in yuvarlama yapmadığını ve Math.round'un sadece en yakın tam sayıya yuvarlandığını unutmayın. Bu nedenle ondalık sayıları korumak için, orijinal sayıyı sıfırları istediğiniz ondalık sayıyı temsil eden on güç ile çarpmamız ve ardından sonucu aynı sayıya bölmemiz gerekir. Sizin durumunuzda: Math.round (43000/80000 * 100 * 100) / 100. Sonuçta her zaman iki ondalık sayı olmasını sağlamak için en sonunda toFixed (2) uygulanabilir (mükemmel, gerektiğinde sondaki sıfırlarla) - mükemmel dikey olarak sunulan bir dizi sayıyı sağa hizalamak için :)
Turbo

7
Ayrıca .toFIxed () işlevinin sayı değil dize verdiğini de unutmayın.
carpiediem

2
Bu hala yuvarlamayı çözmüyor 1.005. (1.005).toFixed(2)hala sonuçlanır 1.00.
DPac

34

Bu işlevi kullan Number(x).toFixed(2);


8
NumberDize olarak döndürülmesini istemiyorsanız, hepsini tekrar sarın :Number(Number(x).toFixed(2));

5
NumberÇağrı, gerekli değildir x.toFixed(2)çalışır.
bgusach

3
@bgusach x.toFixed (2) ifadesi bir sayı değil, dize döndürdüğünden beri sayı çağrısı gerekir. Tekrar sayıya dönüştürmek için Number
Mohan Ram

2
Bu yöntemi kullanırken (1).toFixed(2)döndürür 1.00, ancak 1bu durumda soru soran gerekir .
Eugene Mala

1
Bu işe yaramazsa, 1.005.toFixed(2)verim "1", olması gerektiği zaman "1.01".
Adam Jagosz

33

2017
Sadece yerel kodu kullan.toFixed()

number = 1.2345;
number.toFixed(2) // "1.23"

Sıkı olmanız ve gerekiyorsa rakam eklemeniz gerekiyorsa, replace

number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');

3
ToFixed yöntemi bir dize döndürür. Bir sayı sonucu istiyorsanız, toFixed sonucunu parseFloat öğesine göndermeniz gerekir.
Zambonilli

@Zambonilli Veya gerekirse 1 ile çarpın. ancak sabit sayı nedeniyle çoğu durumda görüntüleme içindir ve hesaplama dizesi için değil doğru biçimdir
pery mimon

2
1; toFixedsizinkinden yıllar önce sadece birden fazla cevapla değil, aynı zamanda sorudaki “sadece gerekirse” koşulunu da sağlayamaz ; askerin istediği yere (1).toFixed(2)verir . "1.00""1"
Mark Amery

Tamam anladım. Bu dava için de bazı çözümler ekliyorum
pery mimon

Lodash kullanıyorsanız, daha da kolay: _.round (sayı, decimalPlace) Son yorumumu sildim, çünkü bir sorunu var. Lodash _.round olsa da çalışır. 1.005 ondalık basamakla 1.01'e dönüşür.
Devin Fields

32

Bu hafif çözümü deneyin :

function round(x, digits){
  return parseFloat(x.toFixed(digits))
}

 round(1.222,  2) ;
 // 1.22
 round(1.222, 10) ;
 // 1.222

Herkes bununla arasında bir fark olup olmadığını biliyor return Number(x.toFixed(digits))mu?

1
@JoeRocc ... görebileceğim kadar fark etmemeli çünkü .toFixed()zaten sadece sayılara izin veriyor.
petermeissner

4
Bu yanıt, bu sayfada birkaç kez belirtildiği gibi aynı soruna sahiptir. Bunun yerine round(1.005, 2)bir sonuç görmeye çalışın . 11.01
MilConDoin

daha yuvarlama algo bir sorun gibi görünüyor? - hayal edebileceğinden daha fazlası var: en.wikipedia.org/wiki/Rounding ... round(0.995, 2) => 0.99; round(1.006, 2) => 1.01; round(1.005, 2) => 1
petermeissner

31

Bunu yapmanın birkaç yolu var. Benim gibi insanlar için Lodash'ın varyantı

function round(number, precision) {
    var pair = (number + 'e').split('e')
    var value = Math.round(pair[0] + 'e' + (+pair[1] + precision))
    pair = (value + 'e').split('e')
    return +(pair[0] + 'e' + (+pair[1] - precision))
}

Kullanımı:

round(0.015, 2) // 0.02
round(1.005, 2) // 1.01

Projeniz jQuery veya lodash kullanıyorsa, round , kütüphanelerde yöntemi .

Güncelleme 1

Varyantı kaldırdım n.toFixed(2), çünkü doğru değil. Teşekkürler @ avalanche1


İkinci seçenek tam olarak iki ondalık basamaklı bir dize döndürür. Soru, yalnızca gerektiğinde ondalık basamak ister. İlk seçenek bu durumda daha iyidir.
Marcos Lima

@MarcosLima Number.toFixed()bir dize döndürür, ancak önündeki artı işaretiyle JS yorumlayıcısı dizeyi bir sayıya dönüştürür. Bu bir sözdizimi şekeri.
stanleyxu2005

Firefox'ta alert((+1234).toFixed(2))"1234.00" gösterilir.
Marcos Lima

Firefox'ta alert(+1234.toFixed(2))atar SyntaxError: identifier starts immediately after numeric literal. Ben 1. seçenek ile sopa.
Marcos Lima

Bu bazı durumlarda işe yaramaz: ile deneyin ( jsfiddle ) 362.42499999999995. (PHP gibi bir sonuç Beklenen echo round(362.42499999999995, 2)): 362.43. Gerçek sonuç:362.42
Dr.Glenluigi Zane Zanettini

26

Lodash kütüphanesi kullanıyorsanız, aşağıdaki gibi lodash'ın yuvarlak yöntemini kullanabilirsiniz.

_.round(number, precision)

Örneğin:

_.round(1.7777777, 2) = 1.78

@Peter Lodash'ın sağladığı işlevsellik seti, standart Javascript ile karşılaştırıldığında gerçekten iyidir. Ancak, Lodash'ın standart JS ile karşılaştırıldığında bazı performans sorunları olduğunu duydum. codeburst.io/…
Madura Pradeep

1
Lodash kullanmanın performans dezavantajları olduğunu kabul ediyorum. Bu konuların birçok soyutlama için ortak olduğunu düşünüyorum. Ancak, bu konu üzerinde kaç cevap olduğuna ve sezgisel çözümlerin uç durumlar için nasıl başarısız olduğuna bakın. Bu kalıbı jQuery ile gördük ve tarayıcılar kullanım durumlarımızın çoğunu çözen ortak bir standart benimsediğinde kök sorunu çözüldü. Performans darboğazları daha sonra tarayıcı motorlarına taşındı. Bence aynı şey karamsar. :)
Peter

26

ES6'dan bu yana, toPrecision kullanarak bunu yapmanın 'uygun' bir yolu vardır (statik değerleri geçersiz kılmadan ve geçici çözümler oluşturmadan)

var x = 1.49999999999;
console.log(x.toPrecision(4));
console.log(x.toPrecision(3));
console.log(x.toPrecision(2));

var y = Math.PI;
console.log(y.toPrecision(6));
console.log(y.toPrecision(5));
console.log(y.toPrecision(4));

var z = 222.987654
console.log(z.toPrecision(6));
console.log(z.toPrecision(5));
console.log(z.toPrecision(4));

o zaman sadece parseFloatsıfırlar 'uzaklaşacaktır'.

console.log(parseFloat((1.4999).toPrecision(3)));
console.log(parseFloat((1.005).toPrecision(3)));
console.log(parseFloat((1.0051).toPrecision(3)));

Yine de '1.005 yuvarlama problemini' çözmez - çünkü şamandıra fraksiyonlarının nasıl işlendiğinin özüdür .

console.log(1.005 - 0.005);

Kütüphanelere açıksanız bignumber.js dosyasını kullanabilirsiniz .

console.log(1.005 - 0.005);
console.log(new BigNumber(1.005).minus(0.005));

console.log(new BigNumber(1.005).round(4));
console.log(new BigNumber(1.005).round(3));
console.log(new BigNumber(1.005).round(2));
console.log(new BigNumber(1.005).round(1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>


3
(1.005).toPrecision(3)aslında 1.00yerine hala geri döner 1.01.
Giacomo

toPrecisionistenen çıktı türünü değiştiren bir dize döndürür.
adamduren

@Giacomo Bu bir .toPrecisionyöntem kusuru değildir , kayan nokta sayılarının ( JS'deki rakamlar) bir özgüllüğüdür - deneyin 1.005 - 0.005, geri dönecektir 0.9999999999999999.
shau-kote

1
(1).toPrecision(3)'1.00' değerini döndürür, ancak 1bu durumda soru soran kişi olmasını istedi .
Eugene Mala

1
@Giacomo'nun dediği gibi, bu cevap "önemli basamakları" "birkaç ondalık basamağa yuvarlama" ile karıştırmaktadır. toPrecisionformatı değil sonuncusunu yapar ve OP'nin sorusuna bir cevap değildir, ancak ilk bakışta alakalı görünmesine rağmen çok yanlış olur. Bkz. En.wikipedia.org/wiki/Significant_figures . Örneğin, Number(123.4).toPrecision(2)iadeler "1.2e+2"ve Number(12.345).toPrecision(2)iadeler "12". Ayrıca @ adamduren'in arzu edilmeyen bir dize (büyük bir sorun değil, arzu edilmeyen) döndürdüğü noktasını kabul ediyorum.
Neek

23

MarkG ve Lavamantis kabul edilenlerden çok daha iyi bir çözüm sundular. Daha fazla oy almadıkları için utanç verici!

İşte kayan nokta ondalık sayı sorunlarını çözmek için kullandığım işlevi de MDN dayalı . Lavamantis'in çözümünden daha genel (ama daha az özlü):

function round(value, exp) {
  if (typeof exp === 'undefined' || +exp === 0)
    return Math.round(value);

  value = +value;
  exp  = +exp;

  if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
    return NaN;

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));

  // Shift back
  value = value.toString().split('e');
  return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}

Şununla kullanın:

round(10.8034, 2);      // Returns 10.8
round(1.275, 2);        // Returns 1.28
round(1.27499, 2);      // Returns 1.27
round(1.2345678e+2, 2); // Returns 123.46

Lavamantis'in çözümü ile karşılaştırıldığında, ...

round(1234.5678, -2); // Returns 1200
round("123.45");      // Returns 123

2
Çözümünüz, MDN çözümünün aksine bazı durumları kapsamaz. Daha kısa olsa da, doğru değil ...
astorije

1
round (-1835.665,2) => -1835.66
Jorge


18

En kolay yaklaşım, toFixed'i kullanmak ve ardından Number işlevini kullanarak sondaki sıfırları şeritlemek olacaktır:

const number = 15.5;
Number(number.toFixed(2)); // 15.5
const number = 1.7777777;
Number(number.toFixed(2)); // 1.78

bu tüm durumlar için geçerli değildir. cevap göndermeden önce kapsamlı testler yapın.
baburao

@baburao Lütfen yukarıdaki çözümün çalışmadığı bir dava gönderin
Marcin Wanago

sabit sayı = 15; Sayı (Number.toFixed (2)); 15 yerine //15.00
Kevin Jhangiani

1
@KevinJhangiani sabit sayısı = 15; Sayı (Number.toFixed (2)); // 15 - Hem en yeni Chrome'da hem de Firefox'ta test ettim
Marcin Wanago

@KevinJhangiani nasılsın 15.00? JS Numaraları do not ondalık basamak depolamak ve herhangi görüntü otomatik aşırı ondalık basamak (sonunda herhangi sıfır) keser.
VLAZ

16
var roundUpto = function(number, upto){
    return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);

toFixed(2) burada 2, bu sayıyı yuvarlamak istediğimiz basamak sayısıdır.


this .toFixed () yönteminin uygulanması daha kolaydır. sadece bir kez geçin.
Ritesh Dhuri


14

En kolay yol:

+num.toFixed(2)

Bir dizeye ve sonra da bir tamsayı / kayan noktaya dönüştürür.


Bu en basit cevap için teşekkürler. Ancak + num'da '+' nedir? Ondalık valin dizeye geldiği yer benim için işe yaramadı. Yaptım: (num * 1) .to (2) düzeltildi.
Ethan

@momo sadece argümanı toFixed()3 olarak değiştir +num.toFixed(3).
Beklendiği

1
@Edmund 1.00
mmm

13

İşte bir prototip yöntemi:

Number.prototype.round = function(places){
    places = Math.pow(10, places); 
    return Math.round(this * places)/places;
}

var yournum = 10.55555;
yournum = yournum.round(2);

13

"ParseFloat (parseFloat (değer) .toFixed (2))" gibi bir şey kullanın

parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78 
parseFloat(parseFloat("10").toFixed(2))-->10 
parseFloat(parseFloat("9.1").toFixed(2))-->9.1

1
yanlışlık şamandıra gösterimine özgü ise değil. sadece kaldırıp tekrar tekrar şamandıra dönüştürerek aynı hatayı yeniden tanıtmak olacaktır!
Ben McIntyre

12

Böyle bir yuvarlamayı yalnızca gerekirse elde etmenin bir yolu Number.prototype.toLocaleString () yöntemini kullanmaktır :

myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})

Bu, tam olarak beklediğiniz çıktıyı sağlar, ancak dizeler olarak. Beklediğiniz veri türü değilse, bu sayıları yine sayılara dönüştürebilirsiniz.


Bu, şimdiye kadar var olan en temiz çözümdür ve tüm karmaşık kayan nokta sorunlarını ortadan kaldırır , ancak her MDN desteği hala eksiktir - Safari toLocaleStringhenüz argümanları geçmeyi desteklememektedir .
Mark Amery

@MarkAmery Şimdilik sadece Android Tarayıcı'nın bazı sorunları var: caniuse.com/#search=toLocaleString
ptyskju

12

Gerçek doğru ondalık yuvarlama hassasiyeti elde etmek için mümkün olan tüm yolların çeşitli yinelemelerinden geçtikten sonra, en doğru ve verimli çözümün Number.EPSILON kullanmak olduğu açıktır. Bu kayan nokta matematik hassasiyeti sorununa gerçek bir matematiksel çözüm sağlar. Burada gösterildiği gibi kolayca çoklu doldurulabilir: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON Kalan tüm IE kullanıcılarını desteklemek için (daha sonra belki biz de bunu yapmayı bırakmalıdır).

Burada sağlanan çözümden uyarlanmıştır: https://stackoverflow.com/a/48850944/6910392

Tam bir kütüphane eklemeden isteğe bağlı bir hassas değişken ile doğru ondalık yuvarlama, döşeme ve tavan sağlayan basit bir çözüm.

GÜNCELLEME: Sergey yorumlarda belirtildiği gibi, bu (veya herhangi bir) yönteme dikkat çekmeye değer bir sınırlama vardır. 0.014999999999999999 gibi sayılar söz konusu olduğunda, kayan nokta değeri depolaması için doğruluk sınırlamalarının mutlak kenarına vurmanın bir sonucu olarak hala yanlışlıklar yaşayacaksınız. Değerin kendisi hemen 0,015 olarak değerlendirildiğinden, bunu hesaba katabilecek herhangi bir matematik veya başka bir çözüm yoktur. Bunu, değeri konsolda tek başına çağırarak onaylayabilirsiniz. Bu sınırlama nedeniyle, dize gösterimi basitçe "0.015" olduğundan, bu değeri azaltmak için dize manipülasyonu kullanmak bile mümkün olmazdı. Bunu hesaba katacak herhangi bir çözümün, değeri bir betiğe kabul etmeden önce verilerin kaynağına mantıksal olarak uygulanması gerekir,

var DecimalPrecision = (function(){
        if (Number.EPSILON === undefined) {
            Number.EPSILON = Math.pow(2, -52);
        }
        this.round = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.round((n + r) * o) / o;
        }
        this.ceil = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.ceil((n + r) * o) / o;
        }
        this.floor = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.floor((n + r) * o) / o;
        }
        return this;
    })();
    console.log(DecimalPrecision.round(1.005));
    console.log(DecimalPrecision.ceil(1.005));
    console.log(DecimalPrecision.floor(1.005));
    console.log(DecimalPrecision.round(1.0049999));
    console.log(DecimalPrecision.ceil(1.0049999));
    console.log(DecimalPrecision.floor(1.0049999));
    console.log(DecimalPrecision.round(2.175495134384,7));
    console.log(DecimalPrecision.round(2.1753543549,8));
    console.log(DecimalPrecision.round(2.1755465135353,4));


1
(DecimalPrecision.round (0.014999999999999999, 2)) // 0,02 döndürür
Sergey

İyi yakalama! Sorun JS'de kayan nokta depolamasıyla ilgili, her zaman bazı uç durumlar olacak. İyi haber, Number.EPSILON'a uyguladığınız matematiğin, bu uç vakaları daha ileriye doğru itmek için daha ince ayarlanabilmesidir. Eğer kenar durumlar için hiçbir imkanı garanti etmek istemiyorsanız, tek gerçek çözüm dizgi manipülasyonu ve daha sonra matematik olacaktır. Değer üzerinde herhangi bir matematiksel hesaplama yaptığınız anda (ondalık işareti taşımaya çalışırken bile), hatayı zaten ürettiniz.
KFish

Aslında, daha fazla incelemede, bu ilgili matematikten değil, belirtilen değer çağrıldığında derhal ortaya çıkar. Bu numarayı konsola yazarak onaylayabilir ve hemen 0.015 olarak değerlendirildiğini görebilirsiniz. Bu nedenle, bu JS'deki herhangi bir kayan nokta sayısı için kesin doğruluk kenarını temsil eder. Bu durumda, dize değeri "0.015" olacağı için dize dönüştürüp değiştiremezsiniz
KFish

11

Bu en basit, daha zarif çözüm (ve ben dünyanın en iyisiyim;):

function roundToX(num, X) {    
    return +(Math.round(num + "e+"+X)  + "e-"+X);
}
//roundToX(66.66666666,2) => 66.67
//roundToX(10,2) => 10
//roundToX(10.904,2) => 10.9

4
Bu, Enotasyonu kullanarak bir argümanı kabul etmek için kabul edilen cevabı yeniden yazmanın güzel bir yoludur .
AxelH

1
Bu bazı durumlarda işe yaramaz: try ( jsfiddle ) roundToX(362.42499999999995, 2). (PHP gibi bir sonuç Beklenen echo round(362.42499999999995, 2)): 362.43. Gerçek sonuç:362.42
Dr.Gulluigi Zane Zanettini

6
IMHO, PHP sonucunuz yanlış. Üçüncü ondalık basamaktan sonra ne olursa olsun, üçüncü ondalık sayı 5'ten düşükse, ikinci ondalık basamak aynı kalmalıdır. Bu matematiksel tanım.
Soldeplata Saketos

1
Daha özlü olmak için "e +" sadece "e" olabilir.
Lonnie Best
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.