Summary
Creating or sending a Zigbee SinglePrecisionFloat with the numeric value 0 can crash an Edge driver with:
[string "st/zigbee/data_types/base_defs/FloatABC.lua"]:201: SinglePrecisionFloat mantissa must be non-negative
Zero is a valid IEEE/ZCL float value and should serialize as exponent field 0, mantissa field 0, and sign bit 0 for positive zero.
This appears to be a bug in the SmartThings Edge Zigbee float data type handling, likely in the path that converts a Lua number into the internal FloatABC representation.
Affected area
st/zigbee/data_types/base_defs/FloatABC.lua
st/zigbee/data_types/SinglePrecisionFloat.lua
SinglePrecisionFloat is Zigbee data type 0x39.
Reproduction
A custom Zigbee Edge driver sends or writes a SinglePrecisionFloat value based on a capability command.
The crash is reproducible when the value is exactly 0, for example:
local data_types = require "st.zigbee.data_types"
local value = data_types.SinglePrecisionFloat(0)
or when a driver command handler eventually writes/sends a Zigbee attribute using:
data_types.SinglePrecisionFloat(0)
Observed in a custom driver for a SiHAS People Counter V2 / CSM-300-ZB device using a custom capability command where value = 0.
Original community report:
https://community.smartthings.com/t/bug-in-floatabc-lua-singleprecisionfloat-mantissa-crash-when-value-is-0/309492
Actual behavior
The driver crashes immediately with:
SinglePrecisionFloat mantissa must be non-negative
The crash happens before the value can be sent to the device.
Expected behavior
SinglePrecisionFloat(0) should be accepted and serialized as positive zero.
For single precision, the resulting payload should be:
The driver should not crash when a valid float value of 0 is used.
Technical analysis
FloatABC.lua already treats zero as a valid special case when reading and presenting float values:
if self.exponent == -1 * self.exponent_modifier and self.mantissa == 0 then
-- Zero mantissa and all 0s exponent represents +/- zero
return 0
end
and similarly in pretty_print().
So the representation expected by the existing implementation is:
exponent = -self.exponent_modifier
mantissa = 0
For SinglePrecisionFloat, where the exponent field is 8 bits, exponent_modifier is 127, so positive zero should be represented internally as:
sign_bit = 0
exponent = -127
mantissa = 0
The likely missing piece is an explicit zero guard in the numeric construction path before normal math.frexp()-based mantissa/exponent normalization is applied.
For zero, math.frexp(0) does not return a normalized mantissa. If the existing conversion path assumes a normal non-zero float and subtracts the implicit leading bit, it can produce a negative mantissa, triggering the current validation error.
Suggested fix
Add explicit handling for numeric zero before the normal float decomposition logic.
Suggested implementation approach:
local function float_from_value(self, value)
if type(value) ~= "number" then
error(string.format("%s values must be numbers", self.NAME), 2)
end
-- Handle zero before math.frexp() normalization.
-- IEEE/ZCL zero: exponent field all zeroes, mantissa field all zeroes.
if value == 0 then
return self(0, -self.exponent_modifier, 0)
end
local sign_bit = value < 0 and 1 or 0
local abs_value = math.abs(value)
local frexp_mantissa, frexp_exponent = math.frexp(abs_value)
-- math.frexp:
-- abs_value = frexp_mantissa * 2^frexp_exponent
--
-- FloatABC normal representation:
-- value = (1 + mantissa) * 2^exponent
--
-- Therefore:
-- exponent = frexp_exponent - 1
-- mantissa = (frexp_mantissa * 2) - 1
local exponent = frexp_exponent - 1
local mantissa = (frexp_mantissa * 2) - 1
return self(sign_bit, exponent, mantissa)
end
If the numeric constructor is currently implemented outside FloatABC.lua, the same zero guard should be added there instead:
if value == 0 then
return FloatType(0, -FloatType.exponent_modifier, 0)
end
Additional hardening
check_mantissa_is_valid() currently rejects mantissas greater than 1, but it should also reject negative mantissas explicitly:
i_table.check_mantissa_is_valid = function(self, mantissa)
if type(mantissa) ~= "number" then
error(string.format("%s mantissa values must be numbers", self.NAME), 2)
elseif mantissa < 0 then
error(string.format("%s mantissa must be non-negative", self.NAME), 2)
elseif mantissa > 1 then
error(string.format("%s mantissa must be less than 1", self.NAME))
end
end
This does not replace the zero fix, but it makes the validation behavior clearer and more complete.
Suggested tests
The following should not throw:
local data_types = require "st.zigbee.data_types"
data_types.SinglePrecisionFloat(0)
data_types.SinglePrecisionFloat(0.0)
data_types.SinglePrecisionFloat(1.0)
data_types.SinglePrecisionFloat(-1.0)
data_types.SinglePrecisionFloat(3.14159)
Positive zero should serialize to:
Existing deserialization and pretty-print behavior for zero should continue to work unchanged.
Impact
Any Edge driver path that constructs a Zigbee SinglePrecisionFloat from a Lua numeric value of exactly 0 may crash.
This can affect custom attributes, manufacturer-specific attributes, or reportable-change/write paths that use ZCL float type 0x39.
It should not be worked around by sending epsilon values such as 0.0000001, because that changes the semantic value. Zero is valid and should be handled by the SDK.
Summary
Creating or sending a Zigbee
SinglePrecisionFloatwith the numeric value0can crash an Edge driver with:Zero is a valid IEEE/ZCL float value and should serialize as exponent field
0, mantissa field0, and sign bit0for positive zero.This appears to be a bug in the SmartThings Edge Zigbee float data type handling, likely in the path that converts a Lua number into the internal
FloatABCrepresentation.Affected area
SinglePrecisionFloatis Zigbee data type0x39.Reproduction
A custom Zigbee Edge driver sends or writes a
SinglePrecisionFloatvalue based on a capability command.The crash is reproducible when the value is exactly
0, for example:or when a driver command handler eventually writes/sends a Zigbee attribute using:
Observed in a custom driver for a SiHAS People Counter V2 / CSM-300-ZB device using a custom capability command where
value = 0.Original community report:
https://community.smartthings.com/t/bug-in-floatabc-lua-singleprecisionfloat-mantissa-crash-when-value-is-0/309492
Actual behavior
The driver crashes immediately with:
The crash happens before the value can be sent to the device.
Expected behavior
SinglePrecisionFloat(0)should be accepted and serialized as positive zero.For single precision, the resulting payload should be:
The driver should not crash when a valid float value of
0is used.Technical analysis
FloatABC.luaalready treats zero as a valid special case when reading and presenting float values:and similarly in
pretty_print().So the representation expected by the existing implementation is:
For
SinglePrecisionFloat, where the exponent field is 8 bits,exponent_modifieris127, so positive zero should be represented internally as:The likely missing piece is an explicit zero guard in the numeric construction path before normal
math.frexp()-based mantissa/exponent normalization is applied.For zero,
math.frexp(0)does not return a normalized mantissa. If the existing conversion path assumes a normal non-zero float and subtracts the implicit leading bit, it can produce a negative mantissa, triggering the current validation error.Suggested fix
Add explicit handling for numeric zero before the normal float decomposition logic.
Suggested implementation approach:
If the numeric constructor is currently implemented outside
FloatABC.lua, the same zero guard should be added there instead:Additional hardening
check_mantissa_is_valid()currently rejects mantissas greater than1, but it should also reject negative mantissas explicitly:This does not replace the zero fix, but it makes the validation behavior clearer and more complete.
Suggested tests
The following should not throw:
Positive zero should serialize to:
Existing deserialization and pretty-print behavior for zero should continue to work unchanged.
Impact
Any Edge driver path that constructs a Zigbee
SinglePrecisionFloatfrom a Lua numeric value of exactly0may crash.This can affect custom attributes, manufacturer-specific attributes, or reportable-change/write paths that use ZCL float type
0x39.It should not be worked around by sending epsilon values such as
0.0000001, because that changes the semantic value. Zero is valid and should be handled by the SDK.