Why does signed 8-bit negative scaler (-3) cause 10^253 overflow and float32 rounding errors in smart metering billing?
-
[!WARNING]
Technical Redaction & Safe Harbor Notice:
All communication captures, system titles, and cryptographic parameters in this thread have been sanitized and neutralized. Real utility meter identifiers, production encryption keys (GUEK/GAK/MK), SIM IMSIs, and proprietary network envelopes have been substituted with standard IEC 62056 synthetic test vectors. Do not post unredacted production captures or live cryptographic keys.We are auditing smart meter billing integration between DLMS/COSEM meters and Head-End MDM systems. When reading instantaneous current (OBIS
1.0.31.7.0.255, L1 Current, Class 3) and accumulated active import energy (OBIS1.0.1.8.0.255, Total Energy, Class 3), we discovered two severe data corruption anomalies across several client parsers:- The $10^{253}$ Value Explosion: For Current L1 (raw value = 5230 mA, expected = 5.230 A), some parsing libraries reported
5.23e+256A! - Billing Drift on High Energy Meters: For industrial meters with cumulative energy > 167,772.16 kWh (raw value =
0x000100000001Wh), converting the integer to standardfloat32resulted in lost energy increments and billing discrepancies!
Here is the raw
Get-Response-Normalfor Current L1 (1.0.31.7.0.255Attribute 2 & 3):00 01 00 01 00 01 00 10 C4 01 C1 00 02 02 12 14 6E 02 02 0F FD 16 21Dissecting the structure:
12 14 6E: Long-Unsigned523002 02: Structure of 2 elements (scaler_unit)0F FD:scaler(Integer8)16 21:unit(Enum 33 = Ampere [A])
Why did the scaler byte
0xFDcause a $10^{253}$ explosion? - The $10^{253}$ Value Explosion: For Current L1 (raw value = 5230 mA, expected = 5.230 A), some parsing libraries reported
-
Sarah, this is the classic Unsigned vs Signed Byte Decoding Bug in A-XDR parsing!
Per DLMS Blue Book Ed. 17 Section 4.3.3 (Register Interface Class 3) and IEC 62056-62 Table 12:
Attribute 3scaler_unitis defined as:ScalerUnit ::= Structure { scaler: Integer8 (Tag 0x0F), // SIGNED 8-bit integer! unit: Enum (Tag 0x16) }In two's complement 8-bit signed arithmetic:
0xFDis $-3$ ($253 - 256 = -3$).
$$\text{Physical Value} = \text{Raw Value} \times 10^{\text{scaler}} = 5230 \times 10^{-3} = 5.230\text{ A}$$If a parser incorrectly reads Tag
0x0Fas an Unsigned Byte (uint8), it treats0xFDas $+253$, calculating:
$$\text{Corrupted Value} = 5230 \times 10^{+253} = 5.23 \times 10^{256}\text{ A}!$$ -
David's mathematical breakdown is spot on. Now let's address the second issue: Float32 Precision Loss on Cumulative Billing Registers.
Smart meters measure cumulative energy in Watt-hours (Wh) or milli-Watt-hours as a 64-bit integer (
Double-Long-Unsigned0x06 orLong64-Unsigned0x15).When a meter accumulates $20{,}000{,}000\text{ Wh}$ ($20{,}000\text{ kWh}$):
- In IEEE 754 Single Precision (
float32), the significand/mantissa is only 24 bits (approx 7.22 decimal digits of precision). - Maximum exact integer in
float32: $2^{24} = 16{,}777{,}216$.
Beyond 16,777,216 Wh,
float32cannot represent individual 1 Wh increments! Adding $1\text{ Wh}$ to $20{,}000{,}000\text{ Wh}$ infloat32results in exact zero change:
$$20{,}000{,}000.0f + 1.0f = 20{,}000{,}000.0f$$
Over time, thousands of kilowatt-hours are lost to rounding! - In IEEE 754 Single Precision (
-
Elena's point regarding IEEE 754 precision is critical for utility MDMS architectures.
The Correct Datatype Standard:
In all billing and data ingestion pipelines:
- Never use
float(32-bit) for energy quantities; - Use
Double(float64: 53-bit mantissa $\implies 9 \times 10^{15}$ exact integers) or Fixed-Point Decimal (Pythondecimal.Decimal, JavaBigDecimal); - Perform all aggregation using raw integer arithmetic before applying the scaler.
- Never use
-
Let's review the standard units table in DLMS Blue Book:
Unit Enum Symbol Physical Quantity Standard Scaler 30 WhActive Energy 0($10^0$)31 kWhActive Energy 3($10^3$)32 varhReactive Energy 033 ACurrent -3(mA $\to$ A) or-235 VVoltage -1(0.1V $\to$ V) or027 WActive Power 0or-1Notice that Voltage and Current scalers are almost ALWAYS negative!
-
Here is how a compliant decoder should parse Tag
0x0F(Integer8):# Compliant Signed Int8 Unpacking: import struct def parse_scaler(scaler_byte): # struct.unpack('b') reads signed 8-bit integer (-128 to +127) return struct.unpack('b', bytes([scaler_byte]))[0] # Testing with 0xFD: scaler = parse_scaler(0xFD) print(f"Parsed Scaler: {scaler}") # Outputs: -3 -
We audited our field data collection scripts and found 3 custom parsers reading
scalerviaord(byte)without signed conversion. Fixing this eliminated the $10^{253}$ current spikes across our entire AMI dashboard! -
And for cumulative energy, we migrated our database schema from
FLOATtoNUMERIC(18, 4)/BIGINT, preserving 100% of Wh billing increments. -
Here is the verified A-XDR payload for Active Energy Import (
1.0.1.8.0.255
00 01 00 01 00 01 00 12 C4 01 C1 00 02 02 06 01 31 2D 00 02 02 0F 00 16 1E06 01 31 2D 00: Double-Long-Unsigned20,000,000 Wh(20,000 kWh)0F 00: Scaler0($10^0$)16 1E: Unit30 [Wh]
-
To synthesize for the community, here is the robust, production-grade Python decoder:
from decimal import Decimal import struct def decode_dlms_register(raw_val_int, scaler_byte, unit_enum): # 1. Decode Scaler as Signed 8-bit Integer scaler = struct.unpack('b', bytes([scaler_byte]))[0] # 2. Use Decimal Arithmetic to prevent Float32 precision loss raw_dec = Decimal(str(raw_val_int)) scale_factor = Decimal(10) ** Decimal(scaler) physical_value = raw_dec * scale_factor print(f"[+] Scaler: {scaler}, Scale Factor: 10^{scaler}") print(f"[+] Exact Physical Value: {physical_value} (Unit Enum: {unit_enum})") return physical_value # Test Case 1: L1 Current (5230 mA, Scaler 0xFD = -3) assert decode_dlms_register(5230, 0xFD, 33) == Decimal("5.230") # Test Case 2: Cumulative Energy (20,000,000 Wh, Scaler 0x00 = 0) assert decode_dlms_register(20000000, 0x00, 30) == Decimal("20000000")Summary Checklist for Register Parsing:
- Signed Scaler: Always unpack Tag
0x0Fas signed 8-bit integer (struct.unpack('b')). - Decimal Arithmetic: Use
Decimalorfloat64to prevent mantissa truncation on high cumulative energy registers (Blue Book Section 4.3.3). - Unit Mapping: Map unit enums to standard SI physical dimensions.
- Signed Scaler: Always unpack Tag
Hello! It looks like you're interested in this conversation, but you don't have an account yet.
Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.
With your input, this post could be even better đ
Register Login