ประเภทข้อมูลลอยและสองครั้งใน Java


220

ชนิดข้อมูลลอยเป็นจุดลอยตัว IEEE 754 32 บิตที่มีความแม่นยำเดียวและชนิดข้อมูลสองเท่าเป็นทศนิยมลอยตัว 64 บิต IEEE 754 ที่มีความแม่นยำสองระดับ

มันหมายความว่าอะไร? และเมื่อใดฉันจึงควรใช้การลอยแทนการใช้สองเท่าหรือในทางกลับกัน


8
คุณควรใช้ลอยแทนการดับเบิลเมื่อการใช้หน่วยความจำมีความสำคัญ หากคุณต้องการการคำนวณที่แม่นยำยิ่งขึ้นใช้คู่ผสม
Everv0id

12
@ Everv0id: ฉันไม่แน่ใจว่าสถานการณ์ใดที่หน่วยความจำแน่นจนต้องเสียสละความแม่นยำสำหรับพื้นที่ (คุณกำลังใช้Javaเพื่อประโยชน์ของความดี ... ) อาจมีบางสถานการณ์ที่มันถูกเรียก แต่ในทางปฏิบัติของฉันฉันเห็นมันไม่ค่อยบ่อย หากคุณต้องการอธิบายอย่างละเอียดว่าทำไมคุณถึงเชื่อว่านี่เป็นความคิดที่ดีการให้คำตอบกับอินสแตนซ์นั้นน่าจะเป็นการเพิ่มเติมที่คุ้มค่า
โกโตะ


5
@ Makoto จริง ๆ แล้วฉันไม่เคยใช้ Floats เพียงเท่าตัว แต่อาจมีแอปพลิเคชั่น (ในทางทฤษฎี) ซึ่งควรเก็บหมายเลขทศนิยมจำนวนมากดังนั้นการใช้หน่วยความจำ 2x อาจมีความสำคัญ ในทางทฤษฎี ofc; ในทางปฏิบัติคุณสามารถซื้อเซิร์ฟเวอร์อื่นได้เสมอ
Everv0id

3
ฉันใช้ตัวเลขความแม่นยำคงที่ 4 ไบต์และ 2 ไบต์เพื่อบันทึกหน่วยความจำ แต่ถ้าคุณมีพันล้านรายการเหล่านี้มันไม่น่าจะคุ้มค่า เวลาที่คุณเขียน "double" แทนที่จะเป็น "float" (มีตัวอักษรอีกหนึ่งตัว) มีค่ามากกว่าหน่วยความจำเสริมที่คุณใช้double1,000 เท่าแต่ถ้าใช้แทนที่จะfloatบันทึกคุณจากข้อผิดพลาดที่เกี่ยวข้องกับความแม่นยำมันก็คุ้มค่า .
Peter Lawrey

คำตอบ:


259

หน้าวิกิพีเดียที่มันเป็นสถานที่ที่ดีที่จะเริ่ม

เพื่อสรุป:

  • floatถูกแสดงใน 32 บิตโดยมี 1 เครื่องหมายบิต, 8 บิตของเลขชี้กำลังและบิต 23 บิตของซิกนิฟิแคนด์ (หรือสิ่งที่ตามมาจากหมายเลขทางวิทยาศาสตร์ - สัญกรณ์: 2.33728 * 10 12 ; 33728 เป็นซิกนิฟิแคนด์)

  • double ถูกแสดงเป็น 64 บิตโดยมี 1 เครื่องหมายบิต, เลขชี้กำลัง 11 บิตและซิกนิฟิแคนด์ 52 บิต

โดยค่าเริ่มต้น Java ใช้doubleเพื่อแสดงตัวเลขจุดลอยตัว (เพื่อ3.14พิมพ์ตัวอักษรdouble) floatนอกจากนี้ยังเป็นชนิดของข้อมูลที่จะทำให้คุณมีช่วงขนาดใหญ่จำนวนมากดังนั้นฉันจะขอแนะนำให้ใช้งานมากกว่า

อาจจะมีบางอย่างที่ห้องสมุดจริงบังคับให้การใช้งานของคุณfloatแต่โดยทั่วไป - ถ้าคุณสามารถรับประกันได้ว่าผลลัพธ์ของคุณจะเพียงพอขนาดเล็กให้พอดีfloat's ช่วงที่กำหนดไว้doubleแล้วจะดีที่สุดในการเลือกด้วย

หากคุณต้องการความแม่นยำ - ตัวอย่างเช่นคุณไม่สามารถมีค่าทศนิยมที่ไม่ถูกต้อง (เช่น1/10 + 2/10) หรือคุณกำลังทำสิ่งใดด้วยสกุลเงิน (เช่นคิดเป็น $ 10.33 ในระบบ) จากนั้นใช้ a BigDecimalซึ่งสามารถรองรับ จำนวนความแม่นยำและการจัดการกับสถานการณ์เช่นนั้นโดยพลการ


4
ไม่ใช่ 233728 == mantissa ในตัวอย่างที่กำหนดหรือไม่ ฉันหมายถึงส่วนอื่น ๆ เก็บไว้ที่ไหนจำนวนเต็ม?
JaLoveAst1k

1
@ mathguy54: ในเอกสารทางวิทยาศาสตร์ 2 จะเป็นจำนวนเต็มทั้งหมดและ. 33728 จะเป็น mantissa นี่คือการอ้างอิงถึงที่
Makoto

5
ฉันกำลังค้นหาข้อมูลเกี่ยวกับการลอยตัวและเพิ่มเป็นสองเท่าและพบสิ่งนี้และต้องการแสดงความคิดเห็น: หากคุณกำลังทำอะไรกับสกุลเงินที่ไม่เกี่ยวข้องกับการเซ็นต์แบบเศษส่วนการใช้ BigDecimal นั้นไร้สาระ สกุลเงินทั่วไปเป็นข้อมูลที่ไม่ต่อเนื่องดังนั้นคุณควรใช้ชนิดข้อมูลจำนวนเต็ม (นี่เป็นหนึ่งในข้อผิดพลาดทั่วไปที่เกิดขึ้นโดยโปรแกรมเมอร์หนุ่ม - เนื่องจากเราใช้ a. เพื่อแยกเงินดอลลาร์ออกจากเซ็นต์พวกเขาคิดว่ามันเป็นค่าเลขทศนิยมมันไม่ใช่)
Trixie Wolf

2
@TrixieWolf คุณจะเจาะจงมากขึ้นคุณเสนอให้ใช้จำนวนเต็มสองจำนวน (ส่วนจำนวนเต็มและส่วนทศนิยม) หรือไม่? และคุณกำลังพูดถึงสกุลเงินทั่วไปส่วนที่เหลือล่ะ เงินบางส่วนจะมีการประเมิน 6 *100ทศนิยมดังนั้นคุณจึงไม่สามารถเพียง กรุณาคุณมีจุดที่นี่ แต่คุณสามารถจะแม่นยำมากขึ้น :)
AxelH

9
@AxelH ยกเว้นเรื่องของการคำนวณทางการเงินที่สามารถมีเซนต์เศษส่วนได้เงินจะไม่ต่อเนื่องกันเสมอ คุณจะใช้ประเภทจำนวนเต็มหนึ่งชนิดในการจัดเก็บข้อมูล ดังนั้น $ 5.34 จะถูกเก็บไว้เป็น 534 ส่วนดอลลาร์คือ val / 100 ในคณิตศาสตร์จำนวนเต็มและเซ็นต์เป็น val% 100 ในคณิตศาสตร์จำนวนเต็มโดยที่% อ้างถึงการดำเนินการที่เหลือ สำหรับเงินที่มีที่มากกว่าทศนิยมควรจะเก็บไว้เป็นส่วนประกอบเพราะมันไม่ต่อเนื่อง แม้ว่ามันจะไม่ต่อเนื่อง แต่บ่อยครั้งที่คุณต้องการกลับไปใช้พื้นที่เก็บข้อมูลแบบไม่ต่อเนื่องส่วนใหญ่เพราะมันแม่นยำดังนั้นคุณจะไม่เสียเงินในการปัดเศษข้อผิดพลาด
Trixie Wolf

72

ลอยช่วยให้คุณประมาณ ความแม่นยำทศนิยม 6-7 หลักในขณะที่สองครั้งจะช่วยให้คุณประมาณ 15-16 นอกจากนี้ช่วงของตัวเลขก็ยิ่งใหญ่ขึ้นเป็นสองเท่า

double ต้องการพื้นที่เก็บข้อมูล 8 ไบต์ในขณะที่ float ต้องการเพียง 4 ไบต์


13

จำนวนจุดลอยตัวหรือที่รู้จักกันว่าตัวเลขจริงใช้ในการประเมินนิพจน์ที่ต้องการความแม่นยำเศษส่วน ตัวอย่างเช่นการคำนวณเช่นรากที่สองหรือยอดเยี่ยมเช่นไซน์และโคไซน์ส่งผลให้ค่าที่มีความแม่นยำต้องใช้ชนิดจุดลอย Java ใช้ชุดมาตรฐาน (IEEE – 754) ประเภท floatingpoint และตัวดำเนินการ มีชนิดจุดลอยสองชนิดคือแบบลอยและแบบคู่ซึ่งเป็นตัวเลขที่มีความแม่นยำเดียวและสองเท่าตามลำดับ ความกว้างและช่วงจะแสดงที่นี่:


   Name     Width in Bits   Range 
    double  64              1 .7e308 to 1.7e+308
    float   32              3 .4e038 to 3.4e+038


ลอย

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

นี่คือตัวอย่างการประกาศตัวแปรลอยตัว:

hightemp ลอย, lowtemp;


สอง

ความแม่นยำสองเท่าซึ่งแสดงโดยคำสำคัญคู่ใช้ 64 บิตเพื่อเก็บค่า ความแม่นยำสองเท่านั้นเร็วกว่าความแม่นยำเดี่ยวในโปรเซสเซอร์สมัยใหม่บางรุ่นที่ได้รับการปรับให้เหมาะสมสำหรับการคำนวณทางคณิตศาสตร์ความเร็วสูง ฟังก์ชันคณิตศาสตร์ยอดเยี่ยมทั้งหมดเช่น sin (), cos () และ sqrt () จะส่งกลับค่าสองเท่า เมื่อคุณต้องการรักษาความแม่นยำในการคำนวณซ้ำหลายครั้งหรือจัดการตัวเลขจำนวนมาก double คือตัวเลือกที่ดีที่สุด


คำตอบนี้ให้ความกระจ่างอย่างชัดเจนเมื่อเราควรใช้ทุ่นและดับเบิลทำไมไม่ทำเช่นนี้?
Ye Win

8
ทั้งfloatมิได้doubleประเภทมีการใช้ที่ดีที่สุดสำหรับสกุลเงินใน Java เพราะพวกเขาเปิดโอกาสสำหรับการปัดเศษข้อผิดพลาด บทความนี้จะกล่าวถึงรายละเอียดเพิ่มเติม: javapractices.com/topic/TopicAction.do?Id=13
PPartisan

1
"โฟลตจะมีประโยชน์เมื่อคิดเป็นดอลลาร์และเซนต์" - ไม่ไม่ไม่ใช่ไม่ใช่ ไม่เคยเก็บสกุลเงินในรูปแบบลอยตัว / เป็นสองเท่า
กิจกรรมลด

2

Java ดูเหมือนว่าจะมีอคติต่อการใช้ double สำหรับการคำนวณอย่างไรก็ตาม:

ในกรณีที่โปรแกรมที่ฉันเขียนไว้ก่อนหน้านี้ในวันนี้วิธีการไม่ทำงานเมื่อฉันใช้ float แต่ตอนนี้ทำงานได้ดีเมื่อฉันแทนที่ float ด้วย double (ใน NetBeans IDE):

package palettedos;
import java.util.*;

class Palettedos{
    private static Scanner Z = new Scanner(System.in);
    public static final double pi = 3.142;

    public static void main(String[]args){
        Palettedos A = new Palettedos();
        System.out.println("Enter the base and height of the triangle respectively");
        int base = Z.nextInt();
        int height = Z.nextInt();
        System.out.println("Enter the radius of the circle");
        int radius = Z.nextInt();
        System.out.println("Enter the length of the square");
        long length = Z.nextInt();
        double tArea = A.calculateArea(base, height);
        double cArea = A.calculateArea(radius);
        long sqArea = A.calculateArea(length);
        System.out.println("The area of the triangle is\t" + tArea);
        System.out.println("The area of the circle is\t" + cArea);
        System.out.println("The area of the square is\t" + sqArea);
    }

    double calculateArea(int base, int height){
        double triArea = 0.5*base*height;
        return triArea;
    }

    double calculateArea(int radius){
        double circArea = pi*radius*radius;
        return circArea;
    }

    long calculateArea(long length){
        long squaArea = length*length;
        return squaArea;
    }
}

ฉันมีปัญหาเดียวกันวันนี้ อะไรคือเหตุผลเบื้องหลังอคตินี้
Shachi

2

สิ่งนี้จะทำให้เกิดข้อผิดพลาด:

public class MyClass {
    public static void main(String args[]) {
        float a = 0.5;
    }
}

/MyClass.java:3: ข้อผิดพลาด: ประเภทที่เข้ากันไม่ได้: การแปลงการสูญเสียที่เป็นไปได้จาก double เป็น float a = 0.5;

นี้จะทำงานได้อย่างสมบูรณ์แบบ

public class MyClass {
    public static void main(String args[]) {
        double a = 0.5;
    }
}

นอกจากนี้ยังจะทำงานได้อย่างสมบูรณ์แบบ

public class MyClass {
    public static void main(String args[]) {
        float a = (float)0.5;
    }
}

เหตุผล : Java โดยค่าเริ่มต้นเก็บตัวเลขจริงเป็นสองเท่าเพื่อให้แน่ใจว่ามีความแม่นยำสูงขึ้น

Double ใช้พื้นที่มากขึ้น แต่แม่นยำยิ่งขึ้นในระหว่างการคำนวณและ float ใช้พื้นที่น้อยลง แต่แม่นยำน้อยกว่า


1

ตามมาตรฐาน IEEE float เป็นตัวแทน 32 บิตของจำนวนจริงในขณะที่ double เป็นตัวแทน 64 บิต

ในโปรแกรมจาวาเรามักจะเห็นการใช้ชนิดข้อมูลคู่ เป็นเพียงการหลีกเลี่ยงการโอเวอร์โฟลว์เนื่องจากช่วงของตัวเลขที่สามารถใช้งานได้โดยใช้ชนิดข้อมูลสองครั้งนั้นมากกว่าช่วงที่ใช้การลอย

นอกจากนี้เมื่อต้องการความแม่นยำสูงก็สนับสนุนให้ใช้ double วิธีการห้องสมุดบางอย่างที่ถูกนำมาใช้มานานแล้วยังต้องการการใช้ชนิดข้อมูลลอยเป็นต้อง (นั่นเป็นเพียงเพราะมันถูกนำมาใช้โดยใช้ลอยไม่มีอะไรอย่างอื่น!)

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


0

ตัวอย่างนี้แสดงวิธีแตกสัญลักษณ์ (บิตซ้ายสุด), เลขชี้กำลัง (บิตต่อไปนี้ 8) และแมนทิสซา (บิตขวา 23) จากโฟลตใน Java

int bits = Float.floatToIntBits(-0.005f);
int sign = bits >>> 31;
int exp = (bits >>> 23 & ((1 << 8) - 1)) - ((1 << 7) - 1);
int mantissa = bits & ((1 << 23) - 1);
System.out.println(sign + " " + exp + " " + mantissa + " " +
  Float.intBitsToFloat((sign << 31) | (exp + ((1 << 7) - 1)) << 23 | mantissa));

วิธีการเดียวกันนี้สามารถใช้ได้กับ double's (exponent 11 บิตและ 52 bit mantissa)

long bits = Double.doubleToLongBits(-0.005);
long sign = bits >>> 63;
long exp = (bits >>> 52 & ((1 << 11) - 1)) - ((1 << 10) - 1);
long mantissa = bits & ((1L << 52) - 1);
System.out.println(sign + " " + exp + " " + mantissa + " " +
  Double.longBitsToDouble((sign << 63) | (exp + ((1 << 10) - 1)) << 52 | mantissa));

เครดิต: http://sj.github.io/java-float/


0

คุณควรใช้ double แทน float สำหรับการคำนวณที่แม่นยำและ float แทน double เมื่อใช้การคำนวณที่แม่นยำน้อยกว่า Float มีเฉพาะเลขทศนิยม แต่ double มีเลขทศนิยมที่มีความแม่นยำสองเท่าของ IEEE754 ทำให้ง่ายต่อการเก็บและคำนวณตัวเลขได้แม่นยำยิ่งขึ้น หวังว่านี่จะช่วยได้


0

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

  • ถ้าเราต้องการใช้จำนวนจุดลอยตัวเป็นชนิดข้อมูลแบบลอยตัวผู้เรียกใช้เมธอดต้องต่อท้าย F หรือ f อย่างชัดเจนเพราะโดยค่าเริ่มต้นทุกหมายเลขจุดลอยตัวจะถือว่าเป็นสองเท่า เป็นการเพิ่มภาระให้โปรแกรมเมอร์ หากเราใช้ตัวเลขทศนิยมเป็นประเภทข้อมูลสองเท่าเราไม่จำเป็นต้องเพิ่มส่วนต่อท้ายใด ๆ
  • โฟลตเป็นชนิดข้อมูลที่มีความแม่นยำเดียวซึ่งหมายความว่ามีขนาด 4 ไบต์ ดังนั้นในการคำนวณขนาดใหญ่เราจะไม่ได้ผลลัพธ์ที่สมบูรณ์ หากเราเลือกประเภทข้อมูลสองเท่าจะมีขนาด 8 ไบต์และเราจะได้ผลลัพธ์ที่สมบูรณ์

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

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