国产精品久久久aaaa,日日干夜夜操天天插,亚洲乱熟女香蕉一区二区三区少妇,99精品国产高清一区二区三区,国产成人精品一区二区色戒,久久久国产精品成人免费,亚洲精品毛片久久久久,99久久婷婷国产综合精品电影,国产一区二区三区任你鲁

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

大促備戰中的隱蔽陷阱:Double轉String會使用科學計數法展示?

京東云 ? 來源:jf_75140285 ? 作者:jf_75140285 ? 2026-01-15 15:04 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

一、背景:大促備戰中的異常數據

大促備戰期間,接到客戶反饋我司上傳到客戶服務器上的文件存在科學計數法表示的情況(下圖的4.55058496E7),與約定不符。

wKgZPGlokWmAG8bqAAKWAO6Bh7A426.png

??

查看轉換前的數據是:455058496,轉換后(除以10:進行毫米到厘米的轉換)就變成了科學計數法形式了。

wKgZO2lokWmAcQ5rAAE3VN848SY583.png

??

問題代碼:

說明:

這個是個EL表達式,含義是使用expr的值作為計算邏輯,計算結果賦值給var指向的變量temp.b,類型是java.lang.String。

?_item代表當前上下文里的一個對象。

?boxLength是_item對象所具備的屬性。

?該表達式先對boxLength執行除以 10 的運算,再把運算結果轉換為字符串(由clazz定義的)。

業務上,boxLength是個長度的概念,單位是毫米,除以10是轉換成厘米的含義。為了保證精度,系統(基于JAVA)會先將boxLength先轉成java.lang.Double類型,再除以10,最后調用Double.toString()方法轉成字符串。

二、問題定位:字符串轉換的科學計數法陷阱

2.1 問題復現

代碼:

Double depthInDouble = 455058496d/10;
log.info("depthInDouble={}", depthInDouble);

結果:

wKgZPGlokWqAd2qbAAA5dUCNPTs551.png

??

2.2 原因分析

問題就出在了最后一行,日志輸出的時候Double會被轉成String,調用Double.toString()方法,而對于Double對象的值在一定的范圍內,會使用科學計數法表示。

log.info的調用鏈(為什么會調用到Double.toStirng()):

log.info("depthInDouble={}", depthInDouble);
  ↓
Log4jLogger.info(String format, Object arg)
  ↓
AbstractLogger.logIfEnabled(...)
  ↓
AbstractLogger.logMessage(...)
  ↓
ParameterizedMessageFactory.newMessage(...)
  ↓
ParameterizedMessage 構造函數(參數被暫存為 Object[])
  ↓
// 此時尚未調用 Double.toString()
  ↓
// 當 Appender 執行輸出時...
Appender.append(LogEvent)
  ↓
LogEvent.getMessage().getFormattedMessage() // 觸發消息格式化
  ↓
ParameterizedMessage.getFormattedMessage()
  ↓
ParameterizedMessage.formatMessage(...)
  ↓
ParameterizedMessage.argToString(Object)
  ↓
Double.toString() // 終于在這里被調用!

查看Double.toString()的源碼,可以看到相關解釋:

wKgZPGlokWuAAmkNAAJuwK22l34806.png

??

也就是說對于極?。ㄐ∮?0^-3)或者極大(大于10^7)值的浮點數,轉成String的時候會使用科學計數法表示,驗證如下。

代碼:

public static void main(String args[]) {
       String depth = "455058496"; // 單位:毫米
       Double depthInDouble = Double.parseDouble(depth)/10;
       String doubleInString = String.valueOf(depthInDouble);
       log.info("depthInDouble={}", depthInDouble);
       log.info("doubleInString={}", doubleInString);
       depthInDouble = 1e-3;
       log.info("10^-3 = {}", depthInDouble);
       depthInDouble = 1e7;
       log.info("10^7 = {}", depthInDouble);
       Double aVerySmallNumber = 1e-9;
       depthInDouble = 1e-3 - aVerySmallNumber;
       log.info("10^-3 - delta = {}", depthInDouble);
       depthInDouble = 1e7 - aVerySmallNumber;
       log.info("10^7 - delta = {}", depthInDouble);
   }

運行結果:

wKgZO2lokWuAGYkJAABf4V6_QsI731.png

??

說明,10^-3不會使用科學記計數法,但是小于它就會使用科學計數法,10^7就會使用科學計數法,小于它就會不會,大于它會。

2.3 為什么要使用科學計數法

2.3.1 小數在計算機內是如何表示的

先不急于討論為什么使用科學計數法,我們先看看小數在計算機內是如何表示的。

從存儲角度來看,計算機的存儲是有限資源,能存儲的數據是有范圍的,不是無限大,也就是說有限的硬件資源限制了計算機可以表示的數值的大小。對于一個浮點數,我們可以用10個bit存儲,也可以用100個,為了實現跨設備、跨平臺的數據統一表示和交換,IEEE 754 規范定義了標準格式,規定了Double類型使用64比特。

wKgZPGlokWyAIlgeAAB73AUrY0o099.png

當64個比特確定了,那么它可以表示的數字的范圍就確定了,接下來考慮怎么表示小數,可以表示什么范圍內的小數,進而再討論威懾么定義超過10^7或者小于10^-3使用科學計數法,而不用普通的方式(定點數表示法)。

類似整數可以利用除以2取余獲得其二級制的表示形式,例如:123(10進制)= 1111011(二進制)

wKgZO2lokW2AaNxmAAubgZjagyA589.png

??

小數則進行乘2取整,如0.123(10進制)= 0. 0001111101(二進制,位數會一直循環無法精確表示,只能近似,這里取了10位)

wKgZO2lokW6AGBorAArNxoeenuw019.png

??

?

因此最簡單的一種設計(不考慮正負)就是將64位中的一部分劃分為整數位,一部分劃分為小數位,比如32位整數,32位小數(定點數表示法)。

那么這樣設計的Double最大數可以表示2^32-1,

如果要以米為單位表示銀河系直徑,約1光年299792458米/秒*1年 = 299792458米/秒*365天*86400秒/天 ≈ 9.45 * 10^15 ,而2^32-1≈4.29 * 10^9 (遠小于1光年),因此無法使用Double表示銀河系直徑,無法支撐天文學科的計算了。

wKgZPGlokW-AS_PZAAVZeZDkbEs624.png

??

這樣設計的Double最小可以表示2^-32=2.38*10^-10 ,一個質子的大小是0.84飛米=8.4*10^-16,因此也無法支持物理學的計算。

所以,矛盾在于增加整數部分的位數,就會壓縮小數部分的位數,不同的領域中,既有要求數字很大可表示的(在乎量級,如天文學、金融學),也有要求數值很小能表示的(在乎精度,如物理學、生物學)。

可以看到,上面的很多數字表達,我們也使用了科學計數法的表示形式來簡化表達,對于上面這個數字(9.454,254,955,488,000)寫起來麻煩還很占地方,而且我們也不需要那么精確,只是看個量級,因此會寫成9.45 * 10^15 ,不影響理解。

即表示一個極大或者極小的數可以使用:【數值*底數^指數】的形式,對于大數來講指數就是正的,小數就是負的,計算機使用二進制,因此底數就是2,所以小數可以表示成:【數值*2^指數】的形式,這個數值,其實就是尾數。

計算機專家們經過多種研究,最終經過IEEE確定了IEEE 754標準,即不確定整數和小數的位數(固定小數點,即定點數),而使用變化的位數,也就是小數點可以浮動,即浮點數表示法。浮點數表示法定義了小數由符號位+指數位+尾數位三部分組成。

符號位是1bit,0代表整數,1代表負數,指數位決定數值的量級,尾數位決定數值精度。

64位的說明如下:

wKgZPGlokXGADR4xAAp3788EyCE054.png

??

?

其中11和52的設計是在平衡了很多需求后得到的最佳實踐。

Double (64位) = 符號位(1位) + 指數位(11位) + 尾數位(52位)

示例:455058496.0 的IEEE 754表示
原始值:455058496.0
二進制科學計數法:1.0101100001110000000000000000000 × 2^28

符號位:0 (正數)
指數位:28 + 1023(偏移量) = 1051 = 10000011011?
尾數位:0101100001110000000000000000000... (52位)

完整64位表示:
0 10000011011 0101100001110000000000000000000000000000000000000000

2.3.2 數值超過10^7或者小于10^-3會發生什么

其實什么也不會發生,只是基于如下原因綜合權衡的結果。

1、認知科學依據

?人類短期記憶的數字處理能力約為7±2位

?超過7位的整數部分難以快速理解

?科學計數法提供更好的可讀性

2、精度保持考慮

?10^7 = 10,000,000 (8位數字)

?超過此值,普通格式會顯得冗長

?10^-3 = 0.001,更小的數用科學計數法更清晰

3、歷史兼容性

?這個標準在多種編程語言中被采用

?保持了與C語言printf的兼容性

?符合IEEE 754標準的建議

這也就是為什么這個這個范圍內的數要表示成科學計數法了。

2.3.3 源碼探究

1、調用鏈路

根據源碼,可以看到Double.toString()方法的調用鏈是:

wKgZO2lokXKAWgnKAAYbGvFATtg163.png

??

分流是否使用科學計數法的核心代碼toChars的代碼如下:

/*
 * Formats the decimal f 10^e.
 */
private int toChars(byte[] str, int index, long f, int e, FormattedFPDecimal fd) {
    /*
     * For details not discussed here see section 10 of [1].
     *
     * Determine len such that
     *     10^(len-1) <= f < 10^len
     */
    int len = flog10pow2(Long.SIZE - numberOfLeadingZeros(f));
    if (f >= pow10(len)) {
        len += 1;
    }
    if (fd != null) {
        fd.set(f, e, len);
        return index;
    }

    /*
     * Let fp and ep be the original f and e, respectively.
     * Transform f and e to ensure
     *     10^(H-1) <= f < 10^H
     *     fp 10^ep = f 10^(e-H) = 0.f 10^e
     */
    f *= pow10(H - len);
    e += len;

    /*
     * The toChars?() methods perform left-to-right digits extraction
     * using ints, provided that the arguments are limited to 8 digits.
     * Therefore, split the H = 17 digits of f into:
     *     h = the most significant digit of f
     *     m = the next 8 most significant digits of f
     *     l = the last 8, least significant digits of f
     *
     * For n = 17, m = 8 the table in section 10 of [1] shows
     *     floor(f / 10^8) = floor(193_428_131_138_340_668 f / 2^84) =
     *     floor(floor(193_428_131_138_340_668 f / 2^64) / 2^20)
     * and for n = 9, m = 8
     *     floor(hm / 10^8) = floor(1_441_151_881 hm / 2^57)
     */
    long hm = multiplyHigh(f, 193_428_131_138_340_668L) >>> 20;
    int l = (int) (f - 100_000_000L * hm);
    int h = (int) (hm * 1_441_151_881L >>> 57);
    int m = (int) (hm - 100_000_000 * h);

    if (0 < e && e <= 7) {
        return toChars1(str, index, h, m, l, e);
    }
    if (-3 < e && e <= 0) {
        return toChars2(str, index, h, m, l, e);
    }
    return toChars3(str, index, h, m, l, e);
}

代碼地址: https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/jdk/internal/math/DoubleToDecimal.java

可以看到使用科學計數法處理的核心代碼是toChars3,代碼如下:

private int toChars3(byte[] str, int index, int h, int m, int l, int e) {
    /* -3 >= e | e > 7: computerized scientific notation */
    index = putDigit(str, index, h);
    index = putChar(str, index, '.');
    index = put8Digits(str, index, m);
    index = lowDigits(str, index, l);
    return exponent(str, index, e - 1);
}

2、toChars3()的參數含義

?byte[] str: 輸出字符串的字節數組

?int index: 當前寫入位置的索引

?int h: 最高位數字 (0-9)

?int m: 中間8位數字 (00000000-99999999)

?int l: 低位數字 (用于精度控制)

?int e: 調整后的十進制指數值

3、 toChars3()的數據流處理步驟

1.putDigit(str, index, h) → 寫入最高位數字

2.putChar(str, index, '.') → 寫入小數點

3.put8Digits(str, index, m) → 寫入中間8位數字

4.lowDigits(str, index, l) → 寫入低位數字(去除尾隨零)

5.exponent(str, index, e-1) → 寫入指數部分

為什么使用 e-1?

原因:已經放置了一位數字在小數點前
目的:調整指數以保持數值不變
示例:4.55058496E7 表示 4.55058496 × 10^7

4、exponent()分析

標準科學計數法:a.bcd × 10^n
約束條件:1 ≤ a < 10(小數點前只有一位非零數字)
private int exponent(byte[] str, int index, int exp) {
    str[index++] = (byte) 'E';  // 寫入字符 'E'
    if (exp < 0) {
        str[index++] = (byte) '-';  // 負指數寫入 '-'
        exp = -exp;  // 轉為正數處理
    }
    if (exp >= 100) {
        str[index++] = (byte) ('0' + exp / 100);  // 百位
        exp %= 100;
    }
    if (exp >= 10) {
        str[index++] = (byte) ('0' + exp / 10);   // 十位
        exp %= 10;
    }
    str[index++] = (byte) ('0' + exp);           // 個位
    return index;
}

?輸入參數: byte[] str(輸出緩沖區)、int index(寫入位置)、int exp(指數值)

?核心功能: 將指數值格式化為字符串并寫入字節數組

?處理邏輯: 優化處理1位、2位、3位數的指數

1. 寫入 'E'
2. 處理負號(如果 exp < 0)
3. 處理百位(如果 exp >= 100)
4. 處理十位(如果 exp >= 10)
5. 處理個位(必須)

?返回值: 更新后的索引位置

例子:

1. 原始數值: 45505849.6
2. 精確指數: 7.658067227112319
3. 調整后指數: 7.658 - 1 = 6.658
4. 四舍五入: 7
5. exponent方法輸入: exp = 7
6. 執行步驟:
   - 寫入 'E' → index = 1
   - exp = 7 < 10,跳過百位和十位
   - 寫入個位 '7' → index = 2
7. 輸出: "E7"
8. 完整結果: "4.55058496E7"

根據源代碼的邏輯簡化了一版如下:

https://coding.jd.com/newJavaEngineerOrientation/Double2String.git

三、解決方案

3.1 BigDecimal 精準控制

new BigDecimal(doubleValue).setScale(2, RoundingMode.HALF_UP).toPlainString() 

3.2 DecimalFormat 格式化

new DecimalFormat("#0.00").format(doubleValue) // 強制保留兩位小數  

四、總結

Double 數值的字符串格式化規則(如 Double.toString())遵循:

?普通格式(Plain):當數值的指數范圍在 [-3, 7) 時(即絕對值在 [10^-3, 10^7) 之間),直接顯示小數形式(如 0.001 或 123456.0)。

?科學計數法(Scientific):當指數范圍超出 [-3, 7)(如 0.000999 或 10000000.0),顯示為科學計數法(如 9.99e-4 或 1.0e7)。

審核編輯 黃宇

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • Doubler
    +關注

    關注

    0

    文章

    8

    瀏覽量

    7352
  • string
    +關注

    關注

    0

    文章

    41

    瀏覽量

    5054
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    C陷阱與缺陷

    程序員繞過編程過程陷阱和障礙。C陷阱與缺陷共分為8章,分別從詞法分析、語法語義、連接、庫函數、預處理器、可移植性缺陷等幾個方面分析了C編程可能遇到的問題。最后,作者用一章的篇幅給
    發表于 09-25 09:51

    科學計數如何轉化為數字。

    LABVIWE如何將科學計數1.3e5,1.6e7,2.7e3,.......,nen轉化為數字?轉化的數字是分開獨立的數字。求大俠指點,謝謝!
    發表于 12-28 22:40

    科學計數轉換

    `如圖所示我的串口通信返回值是以ASCII字符串的科學計數法數值,本人想轉換成浮點型的數值,方便接下來的工作。謝謝!`
    發表于 08-25 21:26

    long double to string函數找不到本地支持

    為long double時,但沒有任何正式文檔,我不能接受它作為一個可行的解決方案。有人遇到過支持長雙倍的函數,并將其轉換成一組ASCII字符,反之亦然?謝謝! 以上來自于百度翻譯 以下為原文 Hi,I
    發表于 11-02 14:58

    IAR debug查看浮點類型變量怎么不用科學計數顯示呢?

    如圖所示的浮點數,科學計數顯示的變量看著難受,不夠直觀有沒有哪個設置可以改變一下的?我自己找了一下沒有找到相關選項.
    發表于 04-14 10:02

    計數器(反饋歸零

    計數器(反饋歸零
    發表于 03-18 20:09 ?5156次閱讀
    <b class='flag-5'>計數</b>器(反饋歸零<b class='flag-5'>法</b>)

    氣相色譜在環境科學的應用

    氣相色譜在環境科學的應用 ⑴大氣微量-氧化碳的分析汽車尾氣中含有一氧化碳,工業鍋爐和家用煤爐燃燒不完全放出
    發表于 12-25 16:54 ?1120次閱讀

    采用歸零的N進制計數器原理

    計數器是一種重要的時序邏輯電路,廣泛應用于各類數字系統。介紹以集成計數器74LS161和74LS160為基礎,用歸零設計N進制計數器的原
    發表于 03-20 10:21 ?95次下載
    采用歸零<b class='flag-5'>法</b>的N進制<b class='flag-5'>計數</b>器原理

    javastring不可變的原因

    ABCabc, 然后又讓s的值為123456。 從打印結果可以看出,s的值確實改變了。那么怎么還說String對象是不可變的呢? 其實這里存在一個誤區: s只是一個String對象的引用,并不是對象本身。對象在內存是一塊內存區
    發表于 09-27 13:24 ?0次下載
    java<b class='flag-5'>中</b><b class='flag-5'>string</b>不可變的原因

    基于隱蔽信息存儲分布的隱蔽信道構造方法

    針對現有的長度式隱蔽信道在信道熵和長度分布特征與合法信道有差異的問題,提出一種基于隱蔽信息存儲分布的隱蔽信道構造方法。將不同編碼方式下的隱蔽
    發表于 01-25 15:37 ?0次下載

    如何使用C語言實現動態擴容的string

    眾所周知,C++ string使用比較方便,關于C++ string源碼實現可以看我的這篇文章:源碼分析C++的string的實現
    的頭像 發表于 10-25 10:59 ?2677次閱讀

    UTF8String是如何編碼的?

    UniversalString和UTF8String 都支持完全相同的字符集,前64K 字符都是BMPString 的字符集。請注意,BMPString 的前128 個字符與IA5String
    的頭像 發表于 08-26 09:55 ?2888次閱讀
    UTF8<b class='flag-5'>String</b>是如何編碼的?

    bigdecimalstring類型

    情況下,我們可能需要將BigDecimal對象轉換為String類型,例如在進行數據的持久化、傳輸、展示等場景。下面將詳細介紹如何在Java中將BigDecimal轉換為String
    的頭像 發表于 11-30 11:09 ?7506次閱讀

    bigdecimalstring類型避免空指針

    在Java,BigDecimal是用于處理高精度數字計算的類。它提供了一種有效的方法來避免使用浮點數的精確度損失問題。然而,在將BigDecimal對象轉換為String類型時,需要特別小心以避
    的頭像 發表于 11-30 11:12 ?4106次閱讀

    FPGA DSP模塊使用的十大關鍵陷阱

    FPGA 芯片中DSP(數字信號處理)硬核是高性能計算的核心資源,但使用不當會引入隱蔽性極強的“坑”。這些坑不僅影響性能和精度,甚至會導致功能錯誤。以下是總結了十大關鍵陷阱及其解決方案,分為 功能正確性、性能優化、系統集成 三個層面。
    的頭像 發表于 01-13 15:18 ?362次閱讀