รักษาความแม่นยำด้วย double ใน Java


149
public class doublePrecision {
    public static void main(String[] args) {

        double total = 0;
        total += 5.6;
        total += 5.8;
        System.out.println(total);
    }
}

พิมพ์รหัสข้างต้น:

11.399999999999

ฉันจะได้รับสิ่งนี้เพื่อพิมพ์ (หรือใช้เป็น) 11.4 ได้อย่างไร


คำตอบ:


151

ตามที่คนอื่นพูดถึงคุณอาจต้องการใช้BigDecimalชั้นเรียนถ้าคุณต้องการให้มีตัวแทน 11.4 ที่แน่นอน

ตอนนี้มีคำอธิบายเล็กน้อยว่าทำไมสิ่งนี้จึงเกิดขึ้น:

floatและdoubleดั้งเดิมประเภทใน Java เป็นจุดลอยตัวเลขจำนวนที่ถูกเก็บไว้เป็นตัวแทนของไบนารีส่วนและตัวแทน

โดยเฉพาะอย่างยิ่งค่าทศนิยมที่มีความแม่นยำสองเท่าเช่นdoubleประเภทคือค่า 64 บิตโดยที่:

  • 1 บิตหมายถึงเครื่องหมาย (บวกหรือลบ)
  • 11 บิตสำหรับเลขชี้กำลัง
  • 52 บิตสำหรับตัวเลขนัยสำคัญ (ส่วนที่เป็นเศษส่วนเป็นไบนารี)

ชิ้นส่วนเหล่านี้รวมกันเพื่อผลิต doubleแสดงค่า

(ที่มา: Wikipedia: ความแม่นยำสองเท่า )

สำหรับคำอธิบายโดยละเอียดเกี่ยวกับวิธีการจัดการค่าจุดลอยตัวใน Java โปรดดูที่ หัวข้อ 4.2.3: ประเภทจุดลอยตัวรูปแบบและค่าของข้อกำหนดภาษา Java

byte, char, int, longประเภทที่มีจุดคงที่ตัวเลขซึ่งเป็น representions ที่แน่นอนของตัวเลข ซึ่งแตกต่างจากหมายเลขจุดคงที่จำนวนจุดลอยตัวจะบางครั้ง (ปลอดภัยที่จะถือว่า "เกือบตลอดเวลา") ไม่สามารถคืนค่าตัวเลขที่แน่นอนได้ นี่คือเหตุผลว่าทำไมคุณจบลงด้วยการ11.399999999999ที่เป็นผลมาจากการ5.6 + 5.8ที่เป็นผลมาจากการ

เมื่อต้องการค่าที่แน่นอนเช่น 1.5 หรือ 150.1005 คุณจะต้องใช้หนึ่งในประเภทจุดคงที่ซึ่งจะสามารถแสดงตัวเลขได้อย่างแน่นอน

ดังที่มีการกล่าวถึงหลายครั้งแล้ว Java มี BigDecimalคลาสที่จะจัดการกับตัวเลขที่มีขนาดใหญ่มากและตัวเลขที่น้อยมาก

จาก Java API Reference สำหรับBigDecimalคลาส:

ตัวเลขทศนิยมที่ไม่เปลี่ยนรูปและไม่แน่นอนและไม่แน่นอน BigDecimal ประกอบด้วยค่า unscaled จำนวนเต็มความแม่นยำโดยพลการและสเกลจำนวนเต็ม 32 บิต ถ้าเป็นศูนย์หรือบวกค่าสเกลคือจำนวนหลักทางด้านขวาของจุดทศนิยม หากลบค่าที่ไม่ปรับสัดส่วนของจำนวนนั้นจะถูกคูณด้วยสิบถึงกำลังของการปฏิเสธของสเกล ค่าของตัวเลขที่แสดงโดย BigDecimal จึงเป็น (unscaledValue × 10 ^ -scale)

มีคำถามมากมายเกี่ยวกับ Stack Overflow ที่เกี่ยวข้องกับเรื่องของจำนวนจุดลอยตัวและความแม่นยำ นี่คือรายการคำถามที่เกี่ยวข้องที่อาจเป็นที่สนใจ:

ถ้าคุณอยากที่จะได้รับลงไปที่รายละเอียด nitty ทรายลอยหมายเลขจุดจะดูที่อะไรทุกนักวิทยาศาสตร์คอมพิวเตอร์ควรรู้เกี่ยวกับจุดลอยเลขคณิต


3
ในความเป็นจริงมักจะมี 53 บิตที่สำคัญเพราะ 1 ก่อนจุด "ทศนิยม" จะบอกเป็นนัยสำหรับทุกคนยกเว้นค่า denormalised ให้ความแม่นยำบิตเพิ่มเติม เช่น 3 ถูกจัดเก็บเป็น (1. ) 1,000 ... x 2 ^ 1 ในขณะที่ 0.5 ถูกจัดเก็บเป็น (1) 0000 ... x 2 ^ -1 เมื่อค่าถูก denormalised (บิตเลขชี้กำลังทั้งหมดเป็นศูนย์) สามารถทำได้ และโดยปกติแล้วจะเป็นตัวเลขที่สำคัญน้อยลงเช่น 1 x 2 ^ -1030 ถูกเก็บเป็น (0) 00000001 x 2 ^ -1022 ดังนั้นตัวเลขเจ็ดที่สำคัญได้ถูกเสียสละเพื่อปรับขนาด
Sarah Phillips

1
ควรสังเกตว่าในขณะที่BigDecimalช้ากว่าdoubleในกรณีนี้มันไม่จำเป็นเนื่องจาก double มีความแม่นยำทศนิยม 15 ตำแหน่งคุณเพียงแค่ปัดเศษ
Peter Lawrey

2
@PeterLawrey มันมีความแม่นยำ15 หลักหากพวกเขาทั้งหมดอยู่ก่อนจุดทศนิยม ทุกสิ่งสามารถเกิดขึ้นได้หลังจากจุดทศนิยมเนื่องจากความไม่เข้ากันของทศนิยมและเศษส่วนไบนารี
มาร์ควิสแห่ง Lorne

@EJP คุณถูกต้องมีความแม่นยำประมาณ 15 หลัก อาจเป็น 16 แต่ปลอดภัยกว่าที่จะคิดว่ามันคือ 15 หรืออาจจะ 14
Peter Lawrey

@PeterLawrey การแก้ไขของ EJP นั้นเกิดจากคำถามของฉัน: stackoverflow.com/questions/36344758/ ...... คุณช่วยกรุณาขยายว่าทำไมมันไม่ถึง 15 อย่างแน่นอนและสถานการณ์อะไรที่สามารถเป็น 16 หรือ 14 ได้?
Shivam Sinha

103

ตัวอย่างเช่นเมื่อคุณใส่ตัวเลขสองเท่า33.33333333333333ค่าที่คุณได้รับนั้นจะเป็นค่าความแม่นยำสองเท่าที่ใกล้เคียงที่สุดซึ่งก็คือ:

33.3333333333333285963817615993320941925048828125

หารด้วย 100 ให้:

0.333333333333333285963817615993320941925048828125

ซึ่งยังไม่สามารถแทนได้ด้วยตัวเลขที่มีความแม่นยำสองเท่าดังนั้นจะถูกปัดเศษเป็นค่าที่สามารถแทนได้ที่ใกล้ที่สุดซึ่งก็คือ:

0.3333333333333332593184650249895639717578887939453125

เมื่อคุณพิมพ์ค่านี้มันจะถูกปัดเศษอีกครั้งเป็นทศนิยม 17 หลักโดยให้:

0.33333333333333326

114
สำหรับทุกคนที่อ่านสิ่งนี้ในอนาคตและงงว่าทำไมคำตอบไม่เกี่ยวข้องกับคำถาม: ผู้ดำเนินรายการบางคนตัดสินใจที่จะรวมคำถามที่ฉัน (และคนอื่น ๆ ) ตอบด้วยคำถามนี้แตกต่างกัน
Stephen Canon

คุณจะรู้ค่า double ที่แน่นอนได้อย่างไร
Michael Yaworski

@mikeyaworski en.wikipedia.org/wiki/Double-precision_floating-point_formatดูตัวอย่างที่มีความแม่นยำสองเท่า
Jaydee

23

ถ้าคุณเพียงต้องการประมวลผลค่าเป็นเศษส่วนคุณสามารถสร้างคลาสเศษส่วนที่เก็บฟิลด์ตัวเศษและส่วน

วิธีการเขียนสำหรับการเพิ่มการลบการคูณและการหารรวมถึงวิธี toDouble วิธีนี้คุณสามารถหลีกเลี่ยงการลอยระหว่างการคำนวณ

แก้ไข: การใช้งานอย่างรวดเร็ว

public class Fraction {

private int numerator;
private int denominator;

public Fraction(int n, int d){
    numerator = n;
    denominator = d;
}

public double toDouble(){
    return ((double)numerator)/((double)denominator);
}


public static Fraction add(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop + bTop, a.denominator * b.denominator);
    }
    else{
        return new Fraction(a.numerator + b.numerator, a.denominator);
    }
}

public static Fraction divide(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.denominator, a.denominator * b.numerator);
}

public static Fraction multiply(Fraction a, Fraction b){
    return new Fraction(a.numerator * b.numerator, a.denominator * b.denominator);
}

public static Fraction subtract(Fraction a, Fraction b){
    if(a.denominator != b.denominator){
        double aTop = b.denominator * a.numerator;
        double bTop = a.denominator * b.numerator;
        return new Fraction(aTop-bTop, a.denominator*b.denominator);
    }
    else{
        return new Fraction(a.numerator - b.numerator, a.denominator);
    }
}

}

1
แน่นอนnumeratorและdenominatorควรเป็นintอย่างไร ทำไมคุณต้องการความแม่นยำจุดลอยตัว?
Samir Talwar

เดาว่าไม่จำเป็นจริงๆ แต่หลีกเลี่ยงการส่งคำสั่งในฟังก์ชัน toDouble เพื่อให้โค้ดอ่านได้ดีขึ้น
Viral Shah

5
ViralShah: นอกจากนี้ยังอาจแนะนำข้อผิดพลาดจุดลอยตัวเมื่อจัดการกับการดำเนินการทางคณิตศาสตร์ ระบุว่าประเด็นของแบบฝึกหัดนี้คือการหลีกเลี่ยงสิ่งนั้นอย่างรอบคอบดูเหมือนจะรอบคอบ
Samir Talwar

แก้ไขเพื่อใช้ ints แทนการเป็นสองเท่าด้วยเหตุผลที่กล่าวถึงโดย Samir Talwar ด้านบน
Viral Shah

3
การใช้เศษส่วนนี้มีปัญหาเนื่องจากไม่ลดลงในรูปแบบที่ง่ายที่สุด 2/3 * 1/2 ให้ 2/6 ซึ่งคุณอยากได้คำตอบที่ 1/3 ในอุดมคติที่คุณต้องการค้นหา gcd ของตัวเศษและตัวหารและหารทั้งสองด้วย
Salix alba

15

สังเกตว่าคุณมีปัญหาเดียวกันหากคุณใช้เลขทศนิยมแบบทศนิยมที่มีความแม่นยำ จำกัด และต้องการจัดการกับ 1/3: 0.333333333 * 3 คือ 0.999999999 ไม่ใช่ไม่ใช่ 1.00000000

น่าเสียดายที่ 5.6, 5.8 และ 11.4 เพียง แต่ไม่ได้ปัดเศษเป็นเลขฐานสองเพราะมันเกี่ยวข้องกับเศษที่ห้า ดังนั้นการแทนค่าแบบลอยของพวกมันจึงไม่ถูกต้องเช่นเดียวกับ 0.3333 ไม่เท่ากับ 1/3

หากตัวเลขทั้งหมดที่คุณใช้เป็นทศนิยมที่ไม่เกิดซ้ำและคุณต้องการผลลัพธ์ที่แน่นอนให้ใช้ BigDecimal หรืออย่างที่คนอื่นพูดถ้าค่าของคุณเป็นเหมือนเงินในแง่ที่ว่าพวกเขาทั้งหมดเป็น 0.01 หรือ 0.001 หรืออะไรก็คูณทุกอย่างด้วยพลังงานคงที่ 10 แล้วใช้ int หรือ long (บวกและลบ) เรื่องไม่สำคัญ: ระวังการคูณ)

แต่ถ้าคุณมีความสุขกับไบนารีการคำนวณ แต่คุณก็ต้องการพิมพ์สิ่งที่ออกในรูปแบบที่เป็นมิตรเล็กน้อยลองหรือjava.util.Formatter String.formatในสตริงรูปแบบระบุความแม่นยำน้อยกว่าความแม่นยำเต็มของสองเท่า ถึงตัวเลขที่มีนัยสำคัญ 10 รายการกล่าวว่า 11.399999999999 คือ 11.4 ดังนั้นผลลัพธ์จะเกือบแม่นยำและอ่านได้ง่ายกว่าในกรณีที่ผลลัพธ์ไบนารีใกล้เคียงกับค่าที่ต้องการทศนิยมเพียงไม่กี่ตำแหน่งเท่านั้น

ความแม่นยำในการระบุนั้นขึ้นอยู่กับจำนวนคณิตศาสตร์ที่คุณทำกับตัวเลขของคุณ - โดยทั่วไปยิ่งคุณทำมากขึ้นเท่าใดข้อผิดพลาดก็จะเพิ่มขึ้น แต่อัลกอริทึมบางตัวจะรวบรวมมันได้เร็วกว่าคนอื่น ๆ ตรงข้ามกับ "เสถียร" ที่เกี่ยวกับข้อผิดพลาดในการปัดเศษ) หากสิ่งที่คุณกำลังทำอยู่คือการเพิ่มค่าบางอย่างแล้วฉันเดาว่าการลดลงเพียงทศนิยมตำแหน่งหนึ่งจะแม่นยำ การทดลอง


3
ไม่ไม่ไม่ใช้คู่กับค่าเงิน! คุณต้องการความแม่นยำด้วยเงินใช้ BigDecimal แทน มิฉะนั้นคำตอบของคุณเป็นสิ่งที่ดี ทุกสิ่งที่คุณต้องการความแม่นยำด้วยใช้ BigDecimal หากความแม่นยำไม่ใช่สิ่งสำคัญคุณสามารถใช้ float หรือ double
MetroidFan2002

1
คำถามไม่ได้กล่าวถึงหรือเกี่ยวข้องกับเงินอีกต่อไป ฉันพูดโดยเฉพาะเพื่อใช้ BigDecimal หรือจำนวนเต็มสำหรับเงิน มีปัญหาอะไร?
Steve Jessop

1
และเท่ากับ "ไม่ใช้สองเท่าสำหรับเงิน" คือ "ไม่ใช้ BigDecimal หรือสองเท่าสำหรับหนึ่งในสาม" แต่บางครั้งปัญหาก็เกี่ยวข้องกับการแบ่งแยกซึ่งในกรณีนี้ฐานทั้งหมดที่ไม่สามารถหารได้ด้วยปัจจัยหลักทั้งหมดของตัวส่วนทั้งหมดนั้นไม่ดีเท่ากัน
Steve Jessop

1
.9999 = 1 หากความแม่นยำของคุณน้อยกว่า 4 หลักสำคัญ
Brian Leahy

9

คุณอาจต้องการใช้ class java.math.BigDecimal ของ java หากคุณต้องการคณิตศาสตร์ที่แม่นยำ นี่เป็นบทความที่ดีจาก Oracle / ดวงอาทิตย์ในกรณีสำหรับ BigDecimal ในขณะที่คุณไม่สามารถแทน 1/3 เป็นคนกล่าวถึงคุณสามารถมีอำนาจตัดสินใจได้อย่างแม่นยำว่าคุณต้องการให้ผลลัพธ์เป็นอย่างไร setScale () เป็นเพื่อนของคุณ .. :)

ตกลงเพราะฉันมีเวลามากเกินไปในมือของฉันในขณะนี้ที่นี่เป็นตัวอย่างรหัสที่เกี่ยวข้องกับคำถามของคุณ:

import java.math.BigDecimal;
/**
 * Created by a wonderful programmer known as:
 * Vincent Stoessel
 * xaymaca@gmail.com
 * on Mar 17, 2010 at  11:05:16 PM
 */
public class BigUp {

    public static void main(String[] args) {
        BigDecimal first, second, result ;
        first = new BigDecimal("33.33333333333333")  ;
        second = new BigDecimal("100") ;
        result = first.divide(second);
        System.out.println("result is " + result);
       //will print : result is 0.3333333333333333


    }
}

และเพื่อเสียบภาษาที่ฉันโปรดปรานใหม่ Groovy นี่คือตัวอย่างของสิ่งที่เหมือนกัน:

import java.math.BigDecimal

def  first =   new BigDecimal("33.33333333333333")
def second = new BigDecimal("100")


println "result is " + first/second   // will print: result is 0.33333333333333

5

ค่อนข้างแน่ใจว่าคุณสามารถทำให้เป็นสามตัวอย่างบรรทัดได้ :)

หากคุณต้องการความแม่นยำที่แน่นอนให้ใช้ BigDecimal มิฉะนั้นคุณสามารถใช้ ints คูณด้วย 10 ^ ความแม่นยำที่คุณต้องการ


5

ดังที่คนอื่น ๆ ระบุไว้ไม่สามารถแสดงค่าทศนิยมทั้งหมดได้ในรูปแบบไบนารีเนื่องจากทศนิยมจะขึ้นอยู่กับกำลังของ 10 และไบนารีจะขึ้นอยู่กับกำลังสอง

หากความแม่นยำมีความสำคัญให้ใช้ BigDecimal แต่ถ้าคุณต้องการเอาต์พุตที่เป็นมิตร:

System.out.printf("%.2f\n", total);

จะให้คุณ:

11.40


5

คุณทำไม่ได้เนื่องจาก 7.3 ไม่มีการแทน จำกัด ในหน่วยไบนารี 2054767329987789/2 ** 48 = 7.3 + 1/1407374883553280 ที่ใกล้เคียงที่สุดคุณจะได้รับคือ

ดูที่http://docs.python.org/tutorial/floatingpoint.htmlสำหรับคำอธิบายเพิ่มเติม (อยู่ในเว็บไซต์ Python แต่ Java และ C ++ มี "ปัญหา" เหมือนกัน)

วิธีการแก้ปัญหาขึ้นอยู่กับว่าปัญหาของคุณคืออะไร:

  • หากคุณไม่ต้องการเห็นตัวเลขเสียงรบกวนทั้งหมดให้แก้ไขการจัดรูปแบบสตริงของคุณ อย่าแสดงมากกว่า 15 หลักสำคัญ (หรือ 7 สำหรับโฟลต)
  • ถ้านั่นคือความไม่แน่นอนของตัวเลขของคุณกำลังทำลายสิ่งต่างๆเช่นคำสั่ง "if" คุณควรเขียน if (abs (x - 7.3) <TOLERANCE) แทน if (x == 7.3)
  • หากคุณกำลังทำงานกับเงินสิ่งที่คุณอาจต้องการคือจุดทศนิยมคงที่ เก็บจำนวนเต็มเซ็นต์หรือหน่วยสกุลเงินที่เล็กที่สุดของคุณ
  • (ไม่เหมือนใครมาก) หากคุณต้องการความแม่นยำมากกว่า 53 บิต (15-16 หลักสำคัญ) ให้ใช้ความแม่นยำสูงชนิดทศนิยมเช่น BigDecimal

7.3 อาจไม่ได้เป็นตัวแทน จำกัด ในไบนารี แต่ผมแน่ใจว่าจะได้รับ -7.3 เมื่อฉันพยายามในสิ่งเดียวกันออกใน C ++
wrongusername

2
ชื่อผู้ใช้ที่ผิด: ไม่คุณไม่ต้องการ มันแสดงวิธีนั้น ใช้รูปแบบ "% .17g" (หรือดีกว่า "% .51g") เพื่อดูคำตอบจริง
dan04

4
private void getRound() {
    // this is very simple and interesting 
    double a = 5, b = 3, c;
    c = a / b;
    System.out.println(" round  val is " + c);

    //  round  val is  :  1.6666666666666667
    // if you want to only two precision point with double we 
            //  can use formate option in String 
           // which takes 2 parameters one is formte specifier which 
           // shows dicimal places another double value 
    String s = String.format("%.2f", c);
    double val = Double.parseDouble(s);
    System.out.println(" val is :" + val);
    // now out put will be : val is :1.67
}

3

ใช้ java.math.BigDecimal

Doubles คือเศษส่วนแบบไบนารีภายในดังนั้นบางครั้งจึงไม่สามารถแสดงเศษส่วนทศนิยมให้เป็นทศนิยมจริงได้


1
-1 สำหรับแนะนำ BigDecimal แบบสุ่ม หากคุณไม่ต้องการเลขทศนิยม (เช่นถ้าคุณกำลังคำนวณด้วยเงิน) BigDecimal จะไม่ช่วยคุณ มันไม่ได้แก้ไขข้อผิดพลาดจุดลอยตัวทั้งหมดของคุณ: คุณยังต้องจัดการกับ 1/3 * 3 = 0.999999999999999999999999999999 และ sqrt (2) ** 2 = 1.999999999999999999999999999999999999999999999999999999999999999999 นอกจากนี้ BigDecimal ยังมีบทลงโทษความเร็วสูงอีกด้วย ที่แย่กว่านั้นเนื่องจากการขาดตัวดำเนินการมากเกินไปของ Java คุณจึงต้องเขียนโค้ดทั้งหมดใหม่
dan04

2
@ dan04 - หากคุณคำนวณด้วยเงินทำไมใช้การแสดงแบบลอยรู้ถึงข้อผิดพลาดโดยธรรมชาติของมัน ... เนื่องจากไม่มีเศษของเซ็นต์คุณสามารถใช้ทศนิยมและคำนวณเซนต์แทนการใช้ดอลลาร์โดยประมาณคุณมีจำนวนเซ็นต์ที่แน่นอน หากคุณต้องการให้เศษส่วนของเซ็นต์นั้นใช้เวลานานและคำนวณเป็นพันเซ็นต์ ยิ่งไปกว่านั้น OP ไม่ได้กล่าวถึงตัวเลขที่ไม่มีเหตุผลเลยทั้งหมดที่เขากังวลก็คือนอกจากนี้ อ่านโพสต์อย่างรอบคอบและเข้าใจปัญหาก่อนที่จะตอบอาจช่วยให้คุณลำบากใจ
Newtopian

3
@ Newtopian: ฉันไม่มีอะไรจะอาย OP ไม่ได้พูดถึงเรื่องเงินและไม่มีข้อบ่งชี้ว่าปัญหาของเขามีค่าทศนิยมโดยธรรมชาติ
dan04

@ dan04 - ไม่มี OP ไม่ได้ ... คุณทำและเสนอความคิดเห็นเชิงบริบทกับสิ่งที่น่าจะเป็นคำตอบที่ได้รับการยอมรับอย่างสมบูรณ์แบบเนื่องจากการให้รายละเอียดที่ไม่ดี
Newtopian

2

คูณทุกอย่างด้วย 100 และเก็บไว้นานเท่าเซ็นต์


2
@Draemon - ดูที่โพสต์ก่อนการแก้ไขครั้งล่าสุด - สิ่งที่ "shoppingTotal" และ "calcGST" และ "calcPST" ดูเหมือนจะเป็นเงินสำหรับฉัน
พอลทอมบลิน

2

คอมพิวเตอร์จัดเก็บตัวเลขเป็นเลขฐานสองและไม่สามารถแสดงตัวเลขได้เช่น 33.333333333 หรือ 100.0 นี่คือหนึ่งในสิ่งที่ยุ่งยากเกี่ยวกับการใช้คู่ คุณจะต้องปัดเศษคำตอบก่อนแสดงต่อผู้ใช้ โชคดีที่แอปพลิเคชั่นส่วนใหญ่คุณไม่จำเป็นต้องมีทศนิยมหลายตำแหน่งเลย


ฉันกำลังคำนวณอัตราต่อรองที่ฉันต้องการให้มีความแม่นยำสูงสุดเท่าที่จะเป็นไปได้ แต่ผมเข้าใจว่ามีข้อ จำกัด
Aly

2

หมายเลขจุดลอยตัวจะแตกต่างจากตัวเลขจริงซึ่งสำหรับหมายเลขจุดลอยตัวที่กำหนดจะมีหมายเลขจุดลอยตัวที่สูงกว่าถัดไป เหมือนกับเลขจำนวนเต็ม ไม่มีจำนวนเต็มระหว่าง 1 ถึง 2

ไม่มีทางที่จะเป็นตัวแทน 1/3 ของการลอยได้ มีทุ่นอยู่ด้านล่างและมีความลอยอยู่เหนือมันและมีระยะห่างระหว่างกัน และ 1/3 อยู่ในพื้นที่นั้น

Apfloat สำหรับ Java อ้างว่าทำงานกับเลขทศนิยมที่มีความแม่นยำโดยพลการ แต่ฉันไม่เคยใช้ อาจจะคุ้มค่าดู http://www.apfloat.org/apfloat_java/

คำถามที่คล้ายกันถูกถามที่นี่ก่อน ห้องสมุด Java ความแม่นยำสูงจุดลอยตัว


1

Doubles เป็นค่าประมาณของจำนวนทศนิยมในแหล่ง Java ของคุณ คุณเห็นผลลัพธ์ของความไม่ตรงกันระหว่าง double (ซึ่งเป็นค่าเลขฐานสอง) และแหล่งที่มาของคุณ (ซึ่งเป็นรหัสทศนิยม)

Java ผลิตใกล้เคียงที่สุดไบนารี คุณสามารถใช้ java.text.DecimalFormat เพื่อแสดงค่าทศนิยมที่ดูดีขึ้น


1

ใช้ BigDecimal นอกจากนี้ยังช่วยให้คุณระบุกฎการปัดเศษ (เช่น ROUND_HALF_EVEN ซึ่งจะลดข้อผิดพลาดทางสถิติโดยการปัดเศษไปยังเพื่อนบ้านคู่หากทั้งคู่มีระยะห่างเท่ากันเช่นทั้ง 1.5 และ 2.5 รอบถึง 2)


1

คำตอบสั้น ๆ : ใช้ BigDecimal ทุกครั้งและให้แน่ใจว่าคุณใช้ Constructor กับอาร์กิวเมนต์Stringไม่ใช่สองครั้งแรก

กลับไปที่ตัวอย่างของคุณรหัสต่อไปนี้จะพิมพ์ 11.4 ตามที่คุณต้องการ

public class doublePrecision {
    public static void main(String[] args) {
      BigDecimal total = new BigDecimal("0");
      total = total.add(new BigDecimal("5.6"));
      total = total.add(new BigDecimal("5.8"));
      System.out.println(total);
    }
}

0

ลองดู BigDecimal มันจัดการปัญหาเกี่ยวกับเลขทศนิยมแบบนั้น

สายใหม่จะมีลักษณะเช่นนี้:

term[number].coefficient.add(co);

ใช้ setScale () เพื่อกำหนดจำนวนตำแหน่งทศนิยมที่จะใช้


0

ทำไมไม่ใช้วิธี round () จากคลาส Math?

// The number of 0s determines how many digits you want after the floating point
// (here one digit)
total = (double)Math.round(total * 10) / 10;
System.out.println(total); // prints 11.4

0

หากคุณไม่มีทางเลือกอื่นนอกจากใช้ค่าสองเท่าสามารถใช้รหัสด้านล่าง

public static double sumDouble(double value1, double value2) {
    double sum = 0.0;
    String value1Str = Double.toString(value1);
    int decimalIndex = value1Str.indexOf(".");
    int value1Precision = 0;
    if (decimalIndex != -1) {
        value1Precision = (value1Str.length() - 1) - decimalIndex;
    }

    String value2Str = Double.toString(value2);
    decimalIndex = value2Str.indexOf(".");
    int value2Precision = 0;
    if (decimalIndex != -1) {
        value2Precision = (value2Str.length() - 1) - decimalIndex;
    }

    int maxPrecision = value1Precision > value2Precision ? value1Precision : value2Precision;
    sum = value1 + value2;
    String s = String.format("%." + maxPrecision + "f", sum);
    sum = Double.parseDouble(s);
    return sum;
}

-1

ไม่ต้องเสีย efford ของคุณโดยใช้ BigDecimal ในกรณี 99.99999% คุณไม่ต้องการมัน java double type เป็นค่าประมาณของ cource แต่ในเกือบทุกกรณีมันแม่นยำพอสมควร ทราบว่าคุณมีข้อผิดพลาดที่เลขนัยสำคัญที่ 14นี่มันเล็กน้อยมากจริง ๆ !

เพื่อให้ได้ผลลัพธ์ที่ดีให้ใช้:

System.out.printf("%.2f\n", total);

2
ฉันคิดว่าเขาเป็นกังวลกับผลลัพธ์ไม่ใช่ความแม่นยำเชิงตัวเลข และ BigDecimal จะไม่ช่วยถ้าคุณเช่น หารด้วยสาม มันสามารถทำให้สิ่งเลวร้ายลงไปอีก ...
Maciek D.

คุณไม่ควรไม่เคยไม่เคยใช้จุดลอยเงิน ฉันเคยเห็นการทำงานซ้ำที่สำคัญบังคับใช้กับผู้รับเหมาที่ฝ่าฝืนกฎนี้แม้จะได้รับคำแนะนำก็ตาม
มาร์ควิสแห่ง Lorne
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.