JavaScript浮動小数点数計算の不正確な問題を解決する方法の分析

3431 ワード

この例では,JavaScriptが浮動小数点数計算の不正確な問題を解決する方法について述べる.皆さんの参考にしてください.具体的には以下の通りです.
最近electronフレームワークを勉強していて、このフレームワークを利用して簡単な計算機demoを作りたいと思っています.小数を演算すると、問題が見つかりました.
0.1+0.2=?
出力は3兆3千億円04銭.
どうしてこんなことになったの?
実際、浮動小数点数の4つの演算については、ほとんどのプログラミング言語で精度誤差のような問題がありますが、C++/C#/Javaなどの言語では精度の問題を避ける方法がパッケージされていますが、JavaScriptは弱いタイプの言語で、設計思想から浮動小数点数に厳しいデータ型はありません.だから精度誤差の問題はとりわけ際立っている.
まず、なぜこの精度誤差が発生したのかを分析します.
まず,0.1+0.2という小児科のように見える問題をコンピュータの観点から考えなければならない.コンピュータで読めるのは10進数ではなくバイナリであることを知っていますので、まず0.1と0.2をバイナリに変換してみましょう.
0.1=>0.0001 1001 1001 1001..0.2=>0.0011 0011 0011...(無限ループ)
0.1と0.2がバイナリに変換された後、無限ループの数字になったことを発見しました.これは現実生活では、無限ループは理解できますが、コンピュータは無限ループを許さず、無限ループの小数に対して、コンピュータは切り込み処理を行います.二重精度浮動小数点数を行う小数点数は52桁までサポートされているため、両者を加算して0.01001100110011001100110011001100110011001100100110011001100という浮動小数点数の小数位の制限で遮断されたバイナリ数字が得られ、このとき、これを10進数に変換すると0.30000000000004になります.
浮動小数点数が発生した原因を知りましたが、この問題をどのように処理しますか?
方法1:toFixed(num)法により小数を保持する.この方法は四捨五入に基づいて小数を保持するので,最後の計算結果は正確ではない.
方法2:計算する数字を(10のn乗を乗じた)コンピュータで正確に認識できる整数にアップグレードし、計算が終わってから降格する方法を推奨します.具体的なコードは以下の通りです(主に3つの方法があります):

/*  obj       */
function isInteger(obj){
  return Math.floor(obj) === obj;
}
/**
*            ,       
*   3.14 》》314    100
*
*/
function toInteger(floatNum){
  var ret = {times:1,num:0};
  //   
  if(isInteger(floatNum)){
    ret.num = floatNum;
    return ret;
  }
  var strfi = floatNum + '';
  //        
  var dotPos = strfi.indexOf('.');
  console.log('dotPos===='+dotPos);
  //       
  var len = strfi.substr(dotPos+1).length;
  console.log('len===='+len);
  //Math.pow(10,len)  10 len  。
  var time = Math.pow(10,len);
  //         
  var intNum = parseInt(floatNum*time + 0.5,10);
  console.log('intNum===='+intNum);
  ret.times = time;
  ret.num = intNum;
  return ret;
}
/**
*    
*                  
*/
function operation(a,b,op){
  var o1 = toInteger(a);
  var o2 = toInteger(b);
  var n1 = o1.num;
  var n2 = o2.num;
  var t1 = o1.times;
  var t2 = o2.times;
  var max = t1 > t2 ? t1 : t2;
  var result = null;
  switch(op){
    case 'add':
      if(t1 === t2){
        result = n1 + n2;
      }else if(t1 > t2){
        result = n1 + n2 * (t1/t2);
      }else{
        result = n1 * (t2/t1) + n2;
      }
      return result / max;
      break;
    case 'subtract':
      if(t1 === t2){
        result = n1 - n2;
      }else if(t1 > t2){
        result = n1 - n2 * (t1/t2);
      }else{
        result = n1 * (t2/t1) - n2;
      }
      return result / max;
      break;
    case 'multiply':
      result = (n1 * n2)/(t1 * t2);
      return result;
      break;
    case 'divide':
      result = (n1 / n2)/(t2 / t1);
      return result;
      break;
  }
}

JavaScriptに関する詳細について興味のある読者は、「JavaScript数学演算用法総括」、「JavaScriptデータ構造とアルゴリズムテクニック総括」、「JavaScript配列操作テクニック総括」、「JavaScriptイベント関連操作とテクニック大全」、「JavaScript操作DOMテクニック総括」および『JavaScript文字と文字列操作テクニックのまとめ』
JavaScriptプログラムの設計に役立つことを願っています.