บทความเทคนิค

precision as displayed ของ HotXLS: กฎการปัดแบบ Excel

precision as displayed ของ Excel ปัดตัวเลขที่เก็บไว้แต่ละตัวให้เหลือเท่าทศนิยมที่ number format ของมันแสดง โดยนับ section ของฟอร์แมตที่ตรงกับเครื่องหมายของค่า บวกสองทศนิยมต่อ % หนึ่งตัว ลบสามต่อคอมมาย่อสเกลหลักพันหนึ่งตัว แล้วปัดครึ่งออกจากศูนย์ HotXLS ใช้กฎเดียวกันนี้ในทั้งสองเอนจิน Delphi เมื่อ TXLSXWorkbook.FullPrecision หรือ TXLSWorkbook.UseFullPrecision เป็น False ฟังดูเหมือนบรรทัดเดียวจบ จนกระทั่งลูกค้ารายงานว่ายอดใบแจ้งหนี้ที่ export ต่างจาก Excel อยู่หนึ่งเซนต์ หรือคอลัมน์ช่วงเวลาในฟอร์แมต [ss].00 ยุบกลายเป็นศูนย์ ทั้งสองเรื่องเกิดขึ้นจริง และทั้งคู่ย้อนกลับไปจนเจอกฎข้อใดข้อหนึ่งข้างต้นที่ทำผิด ตั้งแต่ v2.384.57 สองเอนจินแชร์ implementation เดียวกัน โดยค่าที่คาดหวังถูกวัดจาก Excel 16 ด้วย Workbook.PrecisionAsDisplayed ที่เปิดอยู่

precision as displayed เปลี่ยนอะไรใน workbook กันแน่

precision as displayed เป็น flag ระดับ workbook เพียงตัวเดียวที่สั่งเอนจินคำนวณให้เก็บตัวเลขตามหน้าตา ไม่ใช่ตามที่คำนวณออกมา ใน UI ของ Excel มันนั่งอยู่ใต้ File, Options, Advanced, “When calculating this workbook” ในชื่อ “Set precision as displayed” บนดิสก์มันคือหนึ่งบิต ไฟล์ BIFF8 แบกมันไว้ใน record CalcPrecision ($000E, [MS-XLS] §2.4.35) ที่ฟิลด์ fFullPrec เป็น 1 เมื่อใช้ full precision ปกติและเป็น 0 เมื่อเปิดตัวเลือกนี้ แพ็กเกจ XLSX แบกมันไว้เป็น attribute fullPrecision ของ element calcPr ใน workbook.xml ที่นิยามใน ECMA-376 Part 1 ค่าเริ่มต้นเป็น true และ fullPrecision="0" คือสวิตช์เปิดการปัด

flag นี้ไม่ใช่การตั้งค่าการแสดงผล ตอนติ๊กกล่อง Excel จะเตือนว่าข้อมูลจะสูญเสียความแม่นยำถาวร และมันหมายความแบบนั้นจริง ๆ ค่าถูกเขียนทับเป็นความละเอียดระดับที่แสดง และเลขที่ถูกตัดไปก็หายไปเลย เอากล่องออกภายหลังก็ไม่ได้เลขเก่าคืน 0.1234 ที่แสดงเป็น 12.3% จะกลายเป็น 0.123 ตลอดไป

HotXLS อ่านและเขียน flag นี้ในทั้งสองฟอร์แมต และเปิดให้เข้าถึงในทั้งสองเอนจิน:

  • TXLSXWorkbook.FullPrecision: Boolean บนเอนจิน XLSX โหลดจากและเซฟลง calcPr/@fullPrecision
  • TXLSWorkbook.UseFullPrecision: Boolean บนเอนจินคลาสสิก (อยู่บน IXLSWorkbook ด้วย) โหลดจากและเซฟลง record CalcPrecision
  • ทั้งคู่ค่าเริ่มต้น True ซึ่งเป็นโหมดปลอดภัยไม่ทำลายข้อมูล และตรงกับค่าเริ่มต้นของ Excel

จุดที่ HotXLS ใช้การปัดก็สำคัญ HotXLS ปัดตรงจุดที่มันคำนวณค่า ผลสูตรแต่ละตัวถูกปัดเป็นความละเอียดระดับที่แสดงก่อนถูกเก็บเป็นค่าแคชของเซลล์ ทั้งระหว่าง Recalculate และระหว่างการประเมินตามสั่ง ค่าคงที่ที่คุณ assign ผ่าน Value ถูกเก็บตรงตามที่ให้ ถ้า output ของคุณต้องทำซ้ำสิ่งที่ Excel เก็บหลังติ๊กกล่อง ปัดค่าคงที่พวกนั้นเองก่อนเขียน เช่น ด้วยฟังก์ชันช่วยที่โชว์ในภายหลัง

Excel ตัดสินจำนวนทศนิยมที่เก็บไว้อย่างไร

Excel หาจำนวนทศนิยมที่เก็บไว้จาก section ของฟอร์แมตที่แสดงค่านั้นจริง ๆ ไม่ใช่จากสตริงฟอร์แมตทั้งสาย กฎด้านล่างวัดจาก Excel 16 และเป็นสิ่งที่ XlsApplyDisplayedPrecision ใน lxNumFormat implement ให้ทั้งสองเอนจินของ HotXLS

  1. เลือก section ตามเครื่องหมาย ฟอร์แมตสอง section ใช้ section ที่สองกับค่าติดลบ ฟอร์แมตสาม section ขึ้นไปใช้ section สองกับค่าติดลบ และ section สามกับค่าศูนย์เป๊ะ ๆ นอกนั้นใช้ section แรก
  2. นับ placeholder ทศนิยม ทุก 0, # หรือ ? ที่อยู่หลังจุดทศนิยมใน section นั้นเพิ่มทศนิยมที่เก็บไว้หนึ่งตำแหน่ง
  3. บวกสองต่อเครื่องหมายเปอร์เซ็นต์ 0.0% แสดง 0.1234 เป็น 12.3% ค่าที่เก็บจึงเป็นหนึ่งในร้อยของสิ่งที่เห็นและเก็บสามทศนิยม ไม่ใช่หนึ่ง
  4. ลบสามต่อคอมมาย่อสเกล คอมมาที่ต่อท้าย placeholder จำนวนเต็มตัวสุดท้าย (0,, 0.0,, 0,.0) หารการแสดงผลด้วย 1000 0.0, แสดง 12345.678 เป็น 12.3 Excel จึงเก็บหนึ่งทศนิยมลบสาม ซึ่งเป็นจำนวนติดลบ ค่าถูกปัดเป็นหลักร้อยแล้วเก็บเป็น 12300 คอมมาที่อยู่ระหว่าง placeholder จำนวนเต็มอย่างใน #,##0 เป็นแค่การจัดกลุ่มหลักธรรมดา ไม่เปลี่ยนอะไร
  5. ปล่อย section ที่ไม่ใช่ตัวเลขตามเดิม section แบบ General วันที่และเวลา (รวม elapsed [h], [mm] กับ [ss]), scientific, fraction, section ข้อความ และ section ที่ไม่มี placeholder ตัวเลขเลย ล้วนคงความละเอียดเต็ม
แผนภาพ HotXLS กฎความละเอียดที่แสดง: เลือก section ของฟอร์แมตตามเครื่องหมายของค่า นับ placeholder ตัวเลขหลังจุดทศนิยม บวกสองทศนิยมต่อเครื่องหมายเปอร์เซ็นต์ ลบสามต่อคอมมาย่อสเกลหลักพันที่ทำให้จำนวนติดลบได้ ข้าม General กับ section วันที่เวลาทั้งหมด แล้วปิดท้ายด้วยการปัดครึ่งออกจากศูนย์
จำนวนหลักมาจาก section ที่ตรงกับเครื่องหมาย บวกสองต่อเปอร์เซ็นต์และลบสามต่อคอมมาย่อสเกล จำนวนที่ติดลบจะปัดเป็นหลักสิบหรือหลักร้อย ส่วน General กับ section วันที่ถูกปล่อยตามเดิม

วัดกับ Excel 16 แล้ว นี่คือค่าที่ทั้งสองเอนจินของ HotXLS เก็บสำหรับผลสูตรหนึ่งตัวในแต่ละฟอร์แมต:

Number formatค่าที่คำนวณค่าที่เก็บกฎที่ใช้
0.0%0.12340.123หนึ่งทศนิยมบวกสองสำหรับเครื่องหมายเปอร์เซ็นต์
02.53ครึ่งออกจากศูนย์ ไม่ใช่ปัดเป็นเลขคู่
0-2.5-3ครึ่งออกจากศูนย์ฝั่งติดลบด้วย
0.00;(0.0)-1.2345-1.2section ติดลบแสดงหนึ่งทศนิยม
0.00;(0.0)1.23451.23section บวกแสดงสองทศนิยม
#,##0.01234.56781234.6คอมมาจัดกลุ่ม ไม่มีการย่อสเกล
0.0,12345.67812300หนึ่งทศนิยมลบสาม: ปัดเป็นหลักร้อย
0.0%;(0.00%)-0.0125-0.0125section ติดลบเก็บสองบวกสองทศนิยม
0.001.0051.01ยอมรับความคลาดเคลื่อนของการแทนค่าแบบไบนารี
0;-0;0.00.51ไม่ใช่ศูนย์ section บวกจึงเป็นผู้ตัดสิน

แถวสุดท้ายเป็นกับดักที่เรียบร้อย ค่า 0.5 ปัดเป็นจำนวนเต็ม และ section ศูนย์ไม่มีวันถูกเรียกใช้ เพราะ Excel เลือก section จากค่าที่คำนวณได้ก่อนปัด ข้อจำกัดตรงไปตรงมาหนึ่งข้อฝั่ง HotXLS: section ถูกเลือกด้วยเครื่องหมายเพียงอย่างเดียว ฟอร์แมตที่ section แบกเงื่อนไขวงเล็บแบบกำหนดเองอย่าง [>=1000] จึงยังถูกแยกด้วยเครื่องหมายอยู่ดี เช็กฟอร์แมตแบบนั้นกับ Excel ถ้ามันสำคัญกับคุณ

ทำไม 1.005 ถึงปัดเป็น 1.01 ไม่ใช่ 1.00

Excel ปัด 1.005 ในเซลล์ 0.00 ขึ้นเป็น 1.01 ทั้งที่ double ที่ใกล้ 1.005 ที่สุดต่ำกว่าจุดกึ่งกลางเล็กน้อย และ HotXLS เข้ากับพฤติกรรมนั้นด้วย tolerance ระดับ ulp ไม่กี่ตัว literal 1.005 แทนค่าใน floating point ไบนารีไม่ได้ double IEEE 754 ที่ใกล้ที่สุดคือ 1.00499999999999989341858963598497211933135986328125 คูณ 100 ได้ 100.49999999999999 สูตรตำราเรียน Floor(x * 100 + 0.5) / 100 จึงคืน 1.00 ซึ่งขัดกับตัวเลขที่ผู้ใช้พิมพ์ กับสิ่งที่ Excel แสดง และกับสิ่งที่ Excel เก็บ

Delphi เติมรสชาติของตัวเองเข้ามาอีก System.Round ปัดครึ่งไปทางเลขคู่ Round(2.5) จึงได้ 2 และ Round(3.5) ได้ 4 นั่นคือ banker's rounding ซึ่งเหมาะกับสถิติแต่ผิดกฎในที่นี่: Excel เก็บ 3 ให้ 2.5 ในเซลล์ 0 และเก็บ -3 ให้ -2.5 implementation ของ HotXLS ทำงานบนค่าสัมบูรณ์ บวก 0.5 พร้อม tolerance สัมพัทธ์ 2-51 คูณค่าที่สเกลแล้ว (ulp ไม่กี่ตัวในขนาดระดับนั้น ไม่เคยน้อยกว่าสอง ulp ของ 1.0) ตัดเศษ สเกลกลับ แล้วคืนเครื่องหมายเดิม ฟังก์ชันต่อไปนี้เป็นภาพประกอบที่สมบูรณ์ในตัวของหลักการนี้ ไม่ใช่โค้ดของไลบรารีเอง และรับจำนวนหลักติดลบสำหรับคอมมาย่อสเกลด้วยวิธีเดียวกัน

แผนภาพการปัดของ HotXLS: 2.5 ปัดครึ่งออกจากศูนย์เป็น 3 และ -2.5 เป็น -3 ซึ่ง System.Round ของ Delphi ให้คำตอบแบบ banker คือ 2 กับ -2 และเมื่อ double ที่ใกล้ 1.005 ที่สุดนั่งต่ำกว่าจุดกึ่งกลางเพียงเล็กน้อย tolerance ระดับ ulp ไม่กี่ตัวคือสิ่งที่เปลี่ยนผลแบบ floor 1.00 เป็นคำตอบของ Excel คือ 1.01
Excel ปัดกรณีครึ่งออกจากศูนย์ และใจกว้างกับความคลาดเคลื่อนของการแทนค่าแบบไบนารีด้วย tolerance เล็ก ๆ ทั้งสองรายละเอียดวัดได้ ข้ามข้อใดข้อหนึ่งไปจะเก็บ 2 ให้ 2.5 หรือเก็บ 1.00 ให้ 1.005 ซึ่งห่างจาก Excel หนึ่งเซนต์พอดี
// ภาพร่างหลักการ: ปัดครึ่งออกจากศูนย์ไปยัง ADigits ตำแหน่งทศนิยม
// พร้อม tolerance ระดับ ulp ไม่กี่ตัว เพื่อให้ 1.005 ได้ 1.01
// ADigits < 0 ปัดเป็นหลักสิบ ร้อย ... ("0.0," ให้ -2)
function RoundAsDisplayed(AValue: Double; ADigits: Integer): Double;
const
  Tolerance = 4.440892098500626E-16; // 2^-51, สอง ulp ของ 1.0
var
  I: Integer;
  Scale, Scaled, Eps: Double;
begin
  Result := AValue;
  if (ADigits < -15) or (ADigits > 14) then
    Exit; // เกินความละเอียดของ double: ปล่อยค่าไว้ตามเดิม
  Scale := 1;
  for I := 1 to Abs(ADigits) do
    Scale := Scale * 10;
  if ADigits >= 0 then
  begin
    if Abs(AValue) > 1E300 / Scale then
      Exit; // สเกลแล้วจะ overflow
    Scaled := Abs(AValue) * Scale;
  end
  else
    Scaled := Abs(AValue) / Scale;
  Eps := Scaled * Tolerance;
  if Eps < Tolerance then
    Eps := Tolerance;
  Scaled := Int(Scaled + 0.5 + Eps); // ครึ่งออกจากศูนย์ ไม่ใช่ Round()
  if ADigits >= 0 then
    Result := Scaled / Scale
  else
    Result := Scaled * Scale;
  if AValue < 0 then
    Result := -Result;
end;

// RoundAsDisplayed(1.005, 2)      = 1.01   (แบบ Floor ได้: 1.00)
// RoundAsDisplayed(2.5, 0)        = 3      (Round ให้: 2)
// RoundAsDisplayed(-2.5, 0)       = -3
// RoundAsDisplayed(0.1234, 3)     = 0.123  ("0.0%": 1 + 2 หลัก)
// RoundAsDisplayed(12345.678, -2) = 12300  ("0.0,": 1 - 3 หลัก)

ตัว tolerance เป็นการถ่วงน้ำหนักที่ตั้งใจไว้ ค่าที่อยู่ต่ำกว่าครึ่งสเต็ปจริง ๆ สอง ulp ก็จะถูกปัดขึ้นเหมือนกัน แต่ในระยะห่างระดับนั้นความต่างแยกจากความคลาดเคลื่อนของการแทนค่าไม่ออก และการถือว่ามันเป็นครึ่งสเต็ปคือสิ่งที่ทำให้ทศนิยมที่พิมพ์เข้าไปทำงานตามที่ผู้ใช้คาด

ก่อน v2.384.57 พลาดอะไรไปบ้าง

ก่อน v2.384.57 เอนจิน XLSX กับเอนจินคลาสสิกแยกกันเขียนโค้ด precision-as-displayed ของตัวเอง และแต่ละฝั่งผิดคนละแบบ ถ้าคุณผลิต workbook โดยเปิดตัวเลือกนี้ นี่คืออาการที่ต้องไปหาในไฟล์ที่บิลด์เก่าผลิต

เอนจิน XLSX: ดูแต่ section แรก ไม่มีเปอร์เซ็นต์ ปัดแบบ banker

เส้นทาง XLSX ตัวเก่าถามจำนวนทศนิยมของสตริงฟอร์แมตทั้งสาย ซึ่งมองแต่ section แรกและเมิน % แล้วปัดด้วย Round 0.1234 ใน 0.0% จึงถูกเก็บเป็น 0.1 ซึ่งคือ 10% แทนที่จะเป็น 12.3% บนหน้าจอ 2.5 ใน 0 ถูกเก็บเป็น 2 แทนที่จะเป็น 3 ค่าติดลบในฟอร์แมตอย่าง 0.00;(0.0) ถูกปัดด้วยสองทศนิยมของ section บวก ตั้งแต่ v2.384.57 เอนจิน XLSX เรียก routine แชร์ตัวเดียวกับเอนจินคลาสสิก ซึ่งรีลีสนั้นก็ได้รองรับคอมมาย่อสเกลเพิ่มเข้ามาด้วย

เอนจินคลาสสิก: TRUE กลายเป็น -1

เอนจินคลาสสิกกันการปัดด้วย VarIsNumeric และ VarIsNumeric คืน True ให้ Variant แบบ varBoolean การแปลง Variant นั้นด้วย Double(V) ให้ -1 เพราะ Boolean True สไตล์ COM ถูกเก็บเป็น -1 สูตรอย่าง =A1>0 ในเซลล์ที่ฟอร์แมต 0.00 จึงออกจากการคำนวณใหม่มาเป็นตัวเลข -1 ตั้งแต่ v2.384.57 ผลลัพธ์ Boolean ถูกคัดออกก่อนเช็กตัวเลขใด ๆ และผลลัพธ์ตรรกะก็ยังเป็นผลลัพธ์ตรรกะในทั้งสองเอนจิน

ฟอร์แมตช่วงเวลาแบบ elapsed ถูกอ่านเป็นสี (v2.384.9)

บั๊กที่สามนั่งอยู่ในโมเดล number format ไม่ใช่ตัวการปัด parser จัดประเภท token ในวงเล็บทุกตัวที่ไม่ใช่เงื่อนไขเป็นสี [h], [mm] กับ [ss] จึงไม่เคยติดป้าย section ของมันเป็นวันที่/เวลา การแสดงผลไม่กระทบ เพราะการฟอร์แมตรันบนเส้นทางแยก แต่ precision as displayed พึ่ง flag นั้นเพื่อข้ามค่าเวลา ช่วงเวลาห้าวินาทีคือ 5/86400 ของวัน ราว 0.0000579 ฟอร์แมตอย่าง [ss].00 จึงดูเหมือนตัวเลขสองทศนิยมธรรมดา เมื่อปิด FullPrecision ช่วงเวลาจึงถูกปัดเหลือ 0.00 วัน ตั้งแต่ v2.384.9 ตัวอักษร h, m หรือ s ตัวเดียวในวงเล็บถูก parse เป็น token ช่วงเวลาแบบ elapsed และ section นั้นถือเป็นวันที่/เวลา รีลิสเดียวกันยังแก้การตรวจจับนาทีใน h:mm ที่ท่อนคั่นระหว่าง token เคยบังชั่วโมงจากสายตา parser

แผนภาพ HotXLS การอ่านผิดของช่วงเวลาแบบ elapsed: ห้าวินาทีถูกเก็บเป็นเศษส่วนของวันที่เล็กจิ๋วในเซลล์ที่ฟอร์แมตด้วย token ss ในวงเล็บ ซึ่ง parser ตัวเก่าอ่านเป็นสีแล้วติดป้ายเป็นตัวเลขสองทศนิยมธรรมดา precision as displayed จึงปัดช่วงเวลาเหลือ 0.00 จนกว่ามันจะถูก parse เป็น section ช่วงเวลาแบบ elapsed
การฟอร์แมตรันบนเส้นทางของตัวเอง เซลล์จึงดูถูกต้องขณะที่ค่าที่เก็บถูกปัดเป็นศูนย์ ตัวอักษร h, m หรือ s ตัวเดียวในวงเล็บเป็น token ช่วงเวลาแบบ elapsed ไม่ใช่สี และ section นั้นคงความละเอียดเต็ม

เปิด precision as displayed ใน HotXLS จาก Delphi

เพื่อให้ได้ค่าที่เก็บเทียบเท่า Excel ตั้ง flag ก่อนการคำนวณใหม่รอบที่ควรให้ผล แล้วค่อยอ่านผลแคชหรือเซฟ บนเอนจิน XLSX FullPrecision เป็น flag ธรรมดา การเปลี่ยนมันไม่ทำให้ผลที่ Recalculate รอบก่อนเก็บไว้เสียผล จึงควรตั้งหลัง Create หรือ Open ทันทีและก่อน Recalculate ครั้งแรก ตัวอย่างใช้สูตรเพราะนั่นคือจุดที่ HotXLS ใช้การปัด:

var
  Wb: TXLSXWorkbook;
  Sh: TXLSXWorksheet;
begin
  Wb := TXLSXWorkbook.Create;
  try
    Sh := Wb.Sheets.Add('Totals');
    Sh.Cells[1, 1].Value := 0.1234;
    Sh.Cells[2, 1].Value := 2.5;
    Sh.Cells[3, 1].Value := 12345.678;

    Sh.Cells[1, 2].Formula := '=A1';
    Sh.Cells[1, 2].NumberFormat := '0.0%';   // แสดง 12.3%
    Sh.Cells[2, 2].Formula := '=A2';
    Sh.Cells[2, 2].NumberFormat := '0';      // แสดง 3
    Sh.Cells[3, 2].Formula := '=A3';
    Sh.Cells[3, 2].NumberFormat := '0.0,';   // แสดง 12.3 (หลักพัน)

    // ต้องตั้งก่อน Recalculate ครั้งแรกบนเอนจิน XLSX
    Wb.FullPrecision := False;
    Wb.Recalculate;

    // ผลแคชตอนนี้ตรงกับ Excel 16: 0.123, 3 กับ 12300
    // ค่าคงที่ในคอลัมน์ A ยังคงความละเอียดเต็ม
    Assert(Abs(Double(Sh.Cells[1, 2].Value) - 0.123) < 1E-12);
    Assert(Double(Sh.Cells[2, 2].Value) = 3);
    Assert(Double(Sh.Cells[3, 2].Value) = 12300);

    Wb.SaveAs('totals.xlsx'); // เขียน <calcPr fullPrecision="0"/>
  finally
    Wb.Free;
  end;
end;

เอนจินคลาสสิกทำงานเหมือนกัน มีความสะดวกเพิ่มหนึ่งอย่าง: การ assign TXLSWorkbook.UseFullPrecision ทำสูตรทุกตัวใน dependency graph เป็น dirty Recalculate รอบหน้าจึงประเมิน workbook ทั้งฉบับใต้กฎใหม่ การเปลี่ยน NumberFormat ขณะเปิดตัวเลือกนี้ก็ทำเซลล์สูตรที่เกี่ยวข้องเป็น dirty ด้วย เพราะฟอร์แมตตอนนี้ตัดสินค่าที่เก็บ เตือนไว้ว่า Recalculate ฝั่งคลาสสิกคืนจำนวนเซลล์สูตรที่ประเมินไม่ได้ ศูนย์แปลว่าสำเร็จ:

var
  Wb: TXLSWorkbook;
  Sh: TXLSWorksheet;
begin
  Wb := TXLSWorkbook.Create;
  try
    Sh := Wb.Sheets.Add;
    Sh.Range['A1', 'A1'].Value := -1.2345;
    Sh.Range['B1', 'B1'].Formula := '=A1';
    Sh.Range['B1', 'B1'].NumberFormat := '0.00;(0.0)';
    Sh.Range['C1', 'C1'].Formula := '=A1<0';
    Sh.Range['C1', 'C1'].NumberFormat := '0.00';

    Wb.UseFullPrecision := False; // ทำสูตรทุกตัวเป็น dirty
    if Wb.Recalculate <> 0 then
      raise Exception.Create('Some formulas could not be evaluated');

    // B1 = -1.2: section ติดลบ "(0.0)" แสดงหนึ่งทศนิยม
    // C1 คงเป็น Boolean True (บิลด์ก่อน v2.384.57 เก็บ -1)
    Wb.SaveAs('report.xls'); // record CalcPrecision ที่ fFullPrec = 0
  finally
    Wb.Free;
  end;
end;

ทั้งสองเอนจินยังเคารพ flag ที่มาพร้อมไฟล์ด้วย เปิด workbook ที่เซฟมาโดยเปิดตัวเลือกนี้ FullPrecision หรือ UseFullPrecision จะเป็น False อยู่แล้ว Recalculate หลังโหลดจึงปัดเป๊ะอย่างที่ Excel จะทำ ถ้าคุณแค่ต้องอ่านตัวเลขที่ Excel เก็บไว้แล้ว ข้ามการคำนวณใหม่ไปได้เลย ตามที่เล่าในการอ่านค่าสูตรแบบแคชโดยไม่คำนวณใหม่ ส่วน serial number กับฟอร์แมตวันที่โต้ตอบกับโมเดลฟอร์แมตที่ขับเคลื่อนการเช็กวันที่/เวลาอย่างไร ดูที่date serial ของ Excel ระบบ 1904 และ numFmt ใน Delphi

ควรเปิด precision as displayed เมื่อไร และไม่ควรเมื่อไร

เปิด precision as displayed เมื่อตัวเลขที่เก็บใน workbook ต้องเท่ากับตัวเลขที่แสดงเท่านั้น และคุณยอมสูญเสียเลขเกินไปตลอดกาล กรณีชอบธรรมคลาสสิกคือตารางการเงินที่คอลัมน์จำนวนที่ปัดแล้วต้องรวมกันได้ยอดรวมที่ปัดแล้วบนหน้าจอ ไม่มีเศษเซนต์แอบแฝงทำให้ยอดรวมเพี้ยนไปหนึ่งในตำแหน่งสุดท้าย การให้ตรงกับ workbook เดิมของลูกค้าที่เปิดตัวเลือกนี้ไว้แล้วก็เป็นเหตุผลที่ดีอีกข้อ HotXLS คง flag ไว้ตลอดการ round-trip จึงไม่มีทางพาข้อมูลของเขากลับไป full precision แบบเงียบ ๆ

เลี่ยงมันในสถานการณ์อื่นเกือบทั้งหมด:

  • ข้อมูลวิศวกรรมและวิทยาศาสตร์ การปัดค่าวัดเพราะใครสักคนเลือกฟอร์แมตสองทศนิยมไว้ทำรายงาน ทำลายข้อมูลที่การเปลี่ยนฟอร์แมตภายหลังไม่มีทางเรียกคืนได้
  • เปอร์เซ็นต์กับฟอร์แมตหยาบ ๆ ฟอร์แมต 0% เก็บทศนิยมของสัดส่วนได้แค่สองตำแหน่ง 0.1234 จึงกลายเป็น 0.12 และทุกสูตรปลายทางที่อ่านเซลล์นี้จะทำงานกับ 0.12
  • การแสดงผลแบบย่อสเกล ฟอร์แมต 0, หรือ 0.0, ที่ใช้โชว์หลักพันจะปัดค่าที่เก็บเป็นหลักพันหรือหลักร้อย ซึ่งแทบไม่ใช่สิ่งที่คนเลือกฟอร์แมตตั้งใจไว้
  • เทมเพลตที่ใช้ร่วมกัน flag เป็นของทั้ง workbook ใครก็ตามที่เพิ่มชีตภายหลังจะสืบทอดพฤติกรรมนี้ โดยมักไม่รู้ว่ามันเปิดอยู่

ถ้าสิ่งที่คุณต้องการจริง ๆ คือผลที่ปัดแล้วในไม่กี่เซลล์ที่ระบุ เขียน ROUND ลงในสูตรพวกนั้นแทน ROUND ชัดเจน อยู่แค่ในเซลล์ เห็นได้สำหรับทุกคนที่อ่านสูตร และถูกประเมินโดยเอนจินสูตรของ HotXLS เหมือนฟังก์ชันอื่น ไม่มี side effect ระดับ workbook

สรุปเรื่อง precision as displayed

  • flag ในไฟล์: CalcPrecision $000E ที่ fFullPrec = 0 ใน BIFF8 ([MS-XLS] §2.4.35), calcPr fullPrecision="0" ใน XLSX (ECMA-376 Part 1)
  • สวิตช์ของ HotXLS: TXLSXWorkbook.FullPrecision := False กับ TXLSWorkbook.UseFullPrecision := False ค่าเริ่มต้นทั้งคู่ True
  • section: เลือกด้วยเครื่องหมายของค่าที่คำนวณ section ที่สามใช้เฉพาะค่าศูนย์เป๊ะ ๆ
  • จำนวนหลัก: placeholder ทศนิยม บวกสองต่อ % ลบสามต่อคอมมาย่อสเกล จำนวนติดลบได้
  • การปัด: ครึ่งออกจากศูนย์พร้อม tolerance ระดับ ulp ไม่กี่ตัว 2.5 ให้ 3, -2.5 ให้ -3 และ 1.005 ให้ 1.01
  • ข้ามไป: General, วันที่/เวลา กับช่วงเวลาแบบ elapsed, scientific, fraction, ข้อความ, Boolean และค่า error
  • ขอบเขตใน HotXLS: ผลสูตรขณะคำนวณ ค่าคงที่เก็บตรงตามที่ assign
  • เอนจิน XLSX: ตั้ง FullPrecision ก่อน Recalculate ครั้งแรก setter ฝั่งคลาสสิกทำสูตรทุกตัว dirty ให้เอง
  • เวอร์ชัน: ตรงกับ Excel 16 ในทั้งสองเอนจินตั้งแต่ v2.384.57 ฟอร์แมตช่วงเวลาแบบ elapsed ได้รับการคุ้มครองตั้งแต่ v2.384.9

HotXLS อ่าน เขียน และคำนวณ workbook XLS กับ XLSX จาก Delphi กับ C++Builder โดยตรง รวมถึงตัวเลือกการคำนวณระดับ workbook ที่เล่าไว้ในนี้ รายละเอียด รุ่น และดาวน์โหลดรุ่นทดลองอยู่ที่หน้า HotXLS Delphi spreadsheet component