2016-12-09 19 views
1

から来る多数の適切な値を返さない私は、次のコードを実行しようとするとExcelが...エクスポートされたCSVはこのようになります値6228480018362050000でのApache POIは、Excelの

Int,Bigint,String 
1,6228480018362050000,Very big 

を提出してい..

InputStream inp = new FileInputStream("/.../test.xlsx"); 
DataFormatter df = new DataFormatter(true); 
df.formatCellValue(WorkbookFactory.create(inp).getSheetAt(0).getRow(1).getCell(1)); 

6228480018362049500は、精度が低下するため間違った数値です。実際の価値を得る方法はありますか?

答えて

5

Excelセルに長い数字を入力すると、その数字は15桁の有効数字に切り捨てられます。これは、Excelが大きな整数のようなものを知らないためです。数値を格納するのは浮動小数点のみです。そしてそれに続くのはIEEE 754 specificationです。しかし、IEEE 754仕様では、浮動小数点数として格納できないものもあります。あなたの例では、6228480018362050000は6.22848001836205E + 018であり、そのまま保存することはできません。 IEEE 754仕様によると、6.2284800183620495E + 018または6228480018362049500になります。

Microsoft's knowledge base「Excelは浮動小数点数の格納方法と計算方法についてIEEE 754仕様に従っているため、Excelは15桁の有効数字のみを格納し、15桁目以降の数字はゼロに変更します。

これはまったく真実ではありません。実際には、Office OpenXML(*.xlsx)では、IEEE 754仕様とではなく、の有効桁数が15桁しか格納されていません。あなたの例では、それは<v>6.2284800183620495E+18</v>を格納します。しかしそれは二次的です。 6.22848001836205E + 018が格納されていても、どこかでこれを浮動小数点に再変換しなければならないため、6.2284800183620495E + 18に戻ってしまうからです。 Excelはブックを開いている間も同じことを行います。 <v>6.2284800183620495E+18</v>を浮動小数点に変換し、を次にに変換すると、15桁の有効数字のみが表示されます。

実際に6228480018362050000をExcelで数値として保存する必要がある場合、Excelと同じ結果を得る唯一の方法はExcelと同じことです。これを行うには、BigDecimalroundの方法を使用できます。これは、精度が設定されたMathContextを使用できます。

例:

import org.apache.poi.ss.usermodel.*; 

import java.io.*; 

import java.math.BigDecimal; 
import java.math.MathContext; 

class ReadExcelBigNumbers { 

public static void main(String[] args) throws Exception{ 

    for (int i = 0; i < 10; i++) { 
    String v = "6.2284800183620" + i + "E+018"; 
    double d = Double.parseDouble(v); 
    System.out.print(v + "\t"); 
    System.out.print(d + "\t"); 
    BigDecimal bd = new BigDecimal(d); 
    v = bd.round(new MathContext(15)).toPlainString(); 
    System.out.println(v); 
    } 

    InputStream inp = new FileInputStream("test.xlsx"); 
    Workbook wb = WorkbookFactory.create(inp); 
    for (int i = 1; i < 9; i++) { 
    double d = wb.getSheetAt(0).getRow(i).getCell(1).getNumericCellValue(); 
    BigDecimal bd = new BigDecimal(d); 
    String v = bd.round(new MathContext(15)).toPlainString(); 
    System.out.println(v); 
    } 
} 
} 

最初の部分のプリント:あり

6.22848001836200E+018 6.2284800183620004E18 6228480018362000000 
6.22848001836201E+018 6.2284800183620096E18 6228480018362010000 
6.22848001836202E+018 6.2284800183620198E18 6228480018362020000 
6.22848001836203E+018 6.2284800183620301E18 6228480018362030000 
6.22848001836204E+018 6.2284800183620403E18 6228480018362040000 
6.22848001836205E+018 6.2284800183620495E18 6228480018362050000 
6.22848001836206E+018 6.2284800183620598E18 6228480018362060000 
6.22848001836207E+018 6.22848001836207E18  6228480018362070000 
6.22848001836208E+018 6.2284800183620803E18 6228480018362080000 
6.22848001836209E+018 6.2284800183620905E18 6228480018362090000 

あなたは違いが浮動小数点値、実際の浮動小数点値IEEE 754規格に従っを望んでいたとのBigDecimalを再フォーマット見ることができます。ご覧のように、6.22848001836207E + 018だけをIEEE 754仕様に従って直接保存することができます。

2番目の部分は、次のExcelシートを使用して同じことを行います。

enter image description here

別の可能な回避策は、ナレッジベースの記事に記載されている:「それから、この現象を回避するには、テキストとしてセルの書式を設定するには数字を入力してください。セルに1,024文字まで表示できます。 "数字が本当に数字ではなく、識別子や数字だけが文字として意味される他の文字列であれば、これは良いことです。このような「テキスト番号」を使用した計算は、浮動小数点に再変換せずに問題を再現することはできません。

+0

私はこれを見渡す時間を過ごすことができますが、すばやく一見良く見えます。ありがとう! – Jackie

+0

丸めが有効数字を追加するように見える理由について詳しく説明できると思いますか?たとえば、オリジナルは15桁の有効数字を持っていますが、保存されている数字は17です。 – Jackie

+0

すべての詳細に入ることはできません。しかし、問題は主に丸めではなく、バイナリシステムで浮動小数点値としていくつかの値を格納する能力です。私は上記の例を与えました。 10進法でも同様の問題があります。 '1/3'のように、有限の桁数で浮動小数点値として' 2/3'を格納することはできません。例えば ​​'22/7'と同じです。 –

2

彼らは単に少数の仕方によって、正確6228480018362049536で同じ内部バイナリ値、二つの異なる小数のプレゼンテーションであり6228480018362050000と6228480018362049500.間の精度の変化なし(損失又は利得)があります。かかわらずセルフォーマットの

、エクセルのみ最初の15桁までディスプレイ(ない「記憶」)は、[1]の右側に任意の数字を丸めます。

しかし、他のアプリケーションやファイル形式は、最初の17桁(またはそれ以上)まで表示されます。これは実際にはIEEE 754標準がすべてのバイナリ値[2]を表すために必要なものです。どうやら、それはApache POIとOpenXMLにも当てはまります。

これを実証するには、以下を実行します。

  1. Excelでは、6228480018362050000と入力します。XMLで保存します。

  2. メモ帳でXMLファイルを開きます。セル/データ要素には6.2284800183620495E + 18という6228480018362049500が表示されます。

  3. ExcelでXMLファイルを開きます。 Excelは数式バーに6228480018362050000と数値として書式設定されたセルを表示します。

Excelがゼロで右に任意の数字を置き換える、最初の15桁の有効数字に(CSVおよびTXTファイルから読み出したものを含む)を手動で入力した数値を切り捨てることは事実です。しかし、Excel VBAはそうではありません。

別のデモンストレーションでは、VBAで次のように入力し、手順を実行します。

Sub doit() 
Range("a1:a2").NumberFormat = "0" 
Range("a1") = CDbl("6228480018362050000") 
Range("a2") = CDbl("6228480018362049536") 
Columns("a").AutoFit 
Range("b2") = "=match(a1,a2,0)" 
End Sub 

A1とA2の表示6228480018362050000. B2は、内部バイナリ値が完全に一致しており、VBAは、最初の15桁の有効数字の後に切り捨てないことを示し、1が表示されていることに注意してください。


説明....

Excelやほとんどのアプリケーションでは、数値を表すためにIEEE 754倍精度を使用します。バイナリ表現は、指数関数の2倍( "ビット")の53の連続する累乗の和である。

したがって、9007199254740992(2^53)までの整数だけを正確に表すことができます。 (但し、エクセルは、15桁の数字の書式設定の制限のため、900719925474099 = 2^53と表示することに注意してください)。

ほとんどの大きな整数は近似できます。

これは、有効数字の数にかかわらず、ほとんどの小数部数にも当てはまります。これは、= 10.1-10が数式バーに0.0999999999999996、小数点以下16桁(15桁)で書式設定されたセルに表示される理由の一部です。


しかし、注意してください:6228480018362050000として表示算出値は実際内部バイナリ値と異なるかもしれません。例えば

、あなたはA2、A1とA2の両方のディスプレイにA1及び式= 6228480018362050000 + 1600に6228480018362050000を入力した場合6228480018362050000.

しかし= MATCH(A1、A2,0)は#N/Aを返し、内部バイナリ値が完全一致ではないことを示します。

、XMLファイルは、実際の内部バイナリ値6228480018362051600.あるA2のための電池要素に対応するデータ要素に6.2284800183620516E + 18を示すであろう、小数で、正確6228480018362051584.

(FYI、ありますExcel等価演算子( "=")は内部のバイナリ値を比較せず、代わりに15桁の有効数字に丸めた値を比較しているので、誤解を招くほどTRUEを返します。

A3にA2とペースト値をコピーすると、= MATCH(A1、A3,0)は#N/Aを返し続けます。 A3の内部値が6228480018362050000のバイナリ表現に変更されました。

私は不思議です。それが実際にあなたが遭遇した謎の問題であり、あなたが誤ってあなたの事例でそれを単純化したのであれば。

これは役に立ちますか?


[1] 2つの例外を持つ内部バイナリ値に影響を及ぼさないセルフォーマット:(1)場合精密ほとんどない推奨されないれ、設定され表示されます。 (2)セルの値が計算され、ワー​​クシートがCSVファイルまたはTXTファイルに保存された後、Excelで再度開くかインポートします。 IEEE 754 17重要桁がに必要な最小であることを指定するが

[2]のみ17重要桁「が格納される」ことを意味するものではないすべてのバイナリ値を表します。上記のように、6228480018362049500は実際には正確に6228480018362049536として保存されます。