前回、JavaScriptで計算する場合の記事を書いた。
その後、例えば小数点の値同士を乗算するような場合に、計算結果がおかしくなる現象が出ていることに気が付いた。
例)
33.3333 x 3.3333 = 111.10988889 とならなければならないのに、
33.3333 x 3.3333 = 111.10988889000001 となってしまうようなエラーである。
で、その原因を調べてみたら以下のようなページがあった。
演算誤差について(10進数と2進数) (unibon)
上記のサイト内の以下の記事に掲載されている関数を加えることで、対応した。
JavaScript で演算結果の誤差を目立たなくする (unibon)
実際のコードは以下の通り。
// JavaScript Document
// 品目毎の金額を計算
function calc_price(){
var num1 = document.frm1['num1'].value;
var num2 = document.frm1['num2'].value;
var num3 = document.frm1['num3'].value;
var num4 = document.frm1['num4'].value;
var num5 = document.frm1['num5'].value;
var num6 = document.frm1['num6'].value;
var num7 = document.frm1['num7'].value;
var tanka1 = document.frm1['tanka1'].value;
var tanka2 = document.frm1['tanka2'].value;
var tanka3 = document.frm1['tanka3'].value;
var tanka4 = document.frm1['tanka4'].value;
var tanka5 = document.frm1['tanka5'].value;
var tanka6 = document.frm1['tanka6'].value;
var tanka7 = document.frm1['tanka7'].value;
if((num1 != "0") || (tanka1 != "0")){
// 数量1や単価1が数値以外の場合は、金額1に0をセット
if((isNaN(num1)) || (isNaN(tanka1))){
document.frm1['price1'].value = 0;
}
// eval() とは、数式を数値に変換するメソッド
//document.frm1['price1'].value = eval(num1) * eval(tanka1);
else{
document.frm1['price1'].value = mul(num1,tanka1);
}
}
if((num2 != "0") || (tanka2 != "0")){
// 数量2や単価2が数値以外の場合は、金額2に0をセット
if((isNaN(num2)) || (isNaN(tanka2))){
document.frm1['price2'].value = 0;
return;
}else{
document.frm1['price2'].value = mul(num2,tanka2);
}
}
if((num3 != "0") || (tanka3 != "0")){
// 数量3や単価3が数値以外の場合は、金額3に0をセット
if((isNaN(num3)) || (isNaN(tanka3))){
document.frm1['price3'].value = 0;
}else{
document.frm1['price3'].value = mul(num3,tanka3);
}
}
if((num4 != "0") || (tanka4 != "0")){
// 数量4や単価4が数値以外の場合は、金額4に0をセット
if((isNaN(num4)) || (isNaN(tanka4))){
document.frm1['price4'].value = 0;
}else{
document.frm1['price4'].value = mul(num4,tanka4);
}
}
if((num5 != "0") || (tanka5 != "0")){
// 数量5や単価5が数値以外の場合は、金額5に0をセット
if((isNaN(num5)) || (isNaN(tanka5))){
document.frm1['price5'].value = 0;
}else{
document.frm1['price5'].value = mul(num5,tanka5);
}
}
if((num6 != "0") || (tanka6 != "0")){
// 数量6や単価6が数値以外の場合は、金額6に0をセット
if((isNaN(num6)) || (isNaN(tanka6))){
document.frm1['price6'].value = 0;
}else{
document.frm1['price6'].value = mul(num6,tanka6);
}
}
if((num7 != "0") || (tanka7 != "0")){
// 数量7や単価7が数値以外の場合は、金額7に0をセット
if((isNaN(num7)) || (isNaN(tanka7))){
document.frm1['price7'].value = 0;
}else{
document.frm1['price7'].value = mul(num7,tanka7);
}
}
/*
* 以下、Javascriptで小数を含む演算結果の誤差を目立たなくする関数
* URL http://www.geocities.co.jp/SiliconValley/4334/unibon/javascript/decimalcalculate.html
*
*/
function decimalOperator(t, a, b) {
var x = "" + a;
var p = x.indexOf(".");
var m;
if (p >= 0) {
m = x.length - (p + 1);
} else {
m = 0;
}
var y = "" + b;
var q = y.indexOf(".");
var n;
if (q >= 0) {
n = y.length - (q + 1);
} else {
n = 0;
}
var k;
var c;
if (t == "+") { // add
k = Math.max(m, n);
c = (a - 0) + (b - 0);
} else if (t == "-") { // sub
k = Math.max(m, n);
c = (a - 0) - (b - 0);
} else if (t == "*") { // mul
k = m + n;
c = (a - 0) * (b - 0);
} else {
return null;
}
var z = "" + c;
if (x.indexOf("e") >= 0 || y.indexOf("e") >= 0 || z.indexOf("e") >= 0) {
return c;
}
var r = z.indexOf(".");
var d;
if (r >= 0) {
var u = z.substring(r + 1 + k, r + 1 + k + 1);
d = z.substring(0, r + 1 + k);
if (u >= "5") {
var e = "";
var w = 1;
for (var i = d.length - 1; i >= 0; i--) {
var g = d.substring(i, i + 1);
if (g >= "0" && g = 10) {
h -= 10;
w++;
}
e = ("" + h) + e;
} else if (w > 0 && (g == "+" || g == "-")) {
e = g + ("" + w) + e;
} else {
e = g + e;
}
}
d = e;
}
} else {
d = z;
}
var f = d - 0;
return f;
}
function add(a, b) {
return decimalOperator("+", a, b);
}
function sub(a, b) {
return decimalOperator("-", a, b);
}
function mul(a, b) {
return decimalOperator("*", a, b);
}
}
フォームからは以下のようにして、上記のコードを呼び出す。(※PHPのSmartyを使用)
<form name="frm1" method="post" action="index.php">
<!-- 注文内容 -->
<table cellspacing="0" class="chumon-naiyou">
<tr>
<td class="chumon-form-category" colspan="6">注文内容</th>
</tr>
<tr>
<th scope="col" class="chumon-form-2" colspan="2">品名</th>
<th scope="col" class="chumon-form-2">数量</th>
<th scope="col" class="chumon-form-2">単価</th>
<th scope="col" class="chumon-form-2">金額</th>
</tr>
<!-- 1 -->
<tr>
<td class="chumon-form-2-left"><span class="red">※</span>1</td>
<td class="chumon-form-2">
<input type="text" name="hinmei1" size="40" value="{$hinmei1|escape}" />
</td>
<td class="chumon-form-2">
<input type="text" name="num1" size="8" maxlength="10" value="{$num1|escape}" onchange="calc_price();" />
</td>
<td class="chumon-form-2">
<input type="text" name="tanka1" size="8" maxlength="10" value="{$tanka1|escape}" onchange="calc_price();" />
</td>
<td class="chumon-form-2">
<input type="text" name="price1" size="16" maxlength="18" value="{$price1|escape}" />
</td>
</tr>
<!-- 以下、省略 -->
</table>
<input type="submit" value="確 認" />
</form>