Почему 10000000000000.126.toString () 1000000000000.127 (и что я могу сделать, чтобы предотвратить это)? - PullRequest
1 голос
/ 03 июля 2011

Почему 10000000000000.126.toString() 1000000000000.127 и 100000000000.126.toString() нет?

Я думаю, что это как-то связано с максимальным значением числа в Js (согласно этому вопросу SO ), но связано ли это с операциями с плавающей запятой и как?

Я спрашиваю, потому что я написал эту функцию для форматирования числа с использованием разделителей тысяч и хотел бы предотвратить это.

function th(n,sep) {
    sep = sep || '.';
    var dec = n.toString().split(/[,.]/),
        nArr = dec[0].split(''),
        isDot = /\./.test(sep);
    return function tt(n) {
              return n.length > 3 ?
               tt(n.slice(0,n.length-3)).concat(n.slice(n.length-3).join('')) :
               [n.join('')]
            ;
        }(nArr)
        .join(sep)
        + (dec[1] ? (isDot?',':'.') + dec[1] : '');
}
sep1000(10000000000000.126); //=> 10.000.000.000.000,127
sep1000(1000000000000.126); //=> 1.000.000.000.000,126

Ответы [ 2 ]

7 голосов
/ 03 июля 2011

Поскольку не все числа могут быть точно представлены с плавающей запятой (JavaScript использует числа IEEE 754 с 64-разрядным форматом двойной точности), возникают ошибки округления. Например:

alert(0.1 + 0.2); // "0.30000000000000004"

Эта проблема существует во всех системах нумерации с ограниченным хранилищем (например, во всех системах нумерации), но мы с вами привыкли иметь дело с нашей десятичной системой (которая не может точно представлять «одну треть») и поэтому некоторые удивляются различных значений, которые форматы с плавающей запятой, используемые компьютерами, не могут точно представить. Именно из-за этого вы видите все больше и больше «десятичных» типов стилей (в Java BigDecimal, в C # decimal и т. Д.), Которые используют наш стиль представления чисел (по цене) и это полезно для приложений, где округление должно более точно соответствовать нашим ожиданиям (например, финансовые приложения).


Обновление : я не пробовал, но вы можете обойти это, немного изменив значения, прежде чем захватывать их строки. Например, это работает с вашим конкретным примером ( живая копия ):

Код:

function display(msg) {
  var p = document.createElement('p');
  p.innerHTML = msg;
  document.body.appendChild(p);
}

function preciseToString(num) {
  var floored = Math.floor(num),
      fraction = num - floored,
      rv,
      fractionString,
      n;

  rv = String(floored);
  n = rv.indexOf(".");
  if (n >= 0) {
    rv = rv.substring(0, n);
  }
  fractionString = String(fraction);
  if (fractionString.substring(0, 2) !== "0.") {
     return String(num); // punt
  }
  rv += "." + fractionString.substring(2);
  return rv;
}

display(preciseToString(10000000000000.126));

Результат:

10000000000000.126953125

... которые затем, конечно, можно обрезать по своему усмотрению. Конечно, важно отметить, что 10000000000000.126953125 != 10000000000000.126. Но я думаю, что корабль уже проплыл (например, Number уже содержал неточное значение), учитывая, что вы видели .127. Я не вижу, чтобы вы знали, что оригинал был только в трех местах, а не с Number.

Я не говорю, что вышеизложенное является в любом случае надежным, вы должны действительно пройти через это, чтобы доказать это (то есть, я ) не делать что-то сутулое там. И опять же, поскольку вы не знаете, где именно закончилась точность, я не уверен, насколько она полезна.

2 голосов
/ 03 июля 2011

Это максимальное количество значащих десятичных цифр, которое может хранить число с плавающей запятой.

Если вы посмотрите на http://en.wikipedia.org/wiki/IEEE_754-2008, вы увидите, что число с плавающей запятой двойной точности (binary64) может хранить около 16 (15,95) десятичных цифр.

Если ваш номер содержит больше цифр, вы фактически теряете точность, как в вашем примере.

...