decimal.util.is_decimal() does not fully validate its documented int/scale contract #55

Closed
opened 2026-08-16 08:30:32 +02:00 by michael · 1 comment
Owner

Problem

decimal/util.lua's M.is_decimal() does not fully validate the
internal decimal representation it documents:

-- Internal: { int = integer, scale = non-negative integer }

Current check:

function M.is_decimal(d)
  return type(d) == "table"
    and type(d.int) == "number"
    and type(d.scale) == "number"
    and d.scale >= 0
end

This accepts values that violate the documented contract, e.g.:

{ int = 1.5, scale = 2 }        -- int not an integer
{ int = 123, scale = 1.5 }      -- scale not an integer
{ int = math.huge, scale = 2 }  -- int not finite

M.is_valid_scale() right next to it already implements the correct
integer check (n % 1 == 0), but is_decimal() doesn't use it for
scale, and has no equivalent check for int at all.

An invalid scale/int can reach decimal/convert.lua's tostring()
(string.format/pow10 assuming an integer digit count) or
decimal/arith.lua's arithmetic, with undefined results instead of a
clean nil, daw_error/v1.

Scope

  • is_decimal() rejects non-integer int
  • is_decimal() rejects non-integer scale (reuse is_valid_scale()
    or equivalent logic)
  • Decide whether non-finite int (NaN/Infinity) needs an explicit
    check or is already excluded by the integer check

Acceptance criteria

  • is_decimal() rejects the three example cases above
  • Existing valid decimals still pass
  • fragjan code . and make test (5.1 and 5.4) pass

Found during daw_i18n/v1 migration review of decimal/ (DAW/daw-lua-common#48)

## Problem `decimal/util.lua`'s `M.is_decimal()` does not fully validate the internal decimal representation it documents: ```lua -- Internal: { int = integer, scale = non-negative integer } ``` Current check: ```lua function M.is_decimal(d) return type(d) == "table" and type(d.int) == "number" and type(d.scale) == "number" and d.scale >= 0 end ``` This accepts values that violate the documented contract, e.g.: ```lua { int = 1.5, scale = 2 } -- int not an integer { int = 123, scale = 1.5 } -- scale not an integer { int = math.huge, scale = 2 } -- int not finite ``` `M.is_valid_scale()` right next to it already implements the correct integer check (`n % 1 == 0`), but `is_decimal()` doesn't use it for `scale`, and has no equivalent check for `int` at all. An invalid scale/int can reach `decimal/convert.lua`'s `tostring()` (string.format/pow10 assuming an integer digit count) or `decimal/arith.lua`'s arithmetic, with undefined results instead of a clean `nil, daw_error/v1`. ## Scope - `is_decimal()` rejects non-integer `int` - `is_decimal()` rejects non-integer `scale` (reuse `is_valid_scale()` or equivalent logic) - Decide whether non-finite `int` (NaN/Infinity) needs an explicit check or is already excluded by the integer check ## Acceptance criteria - [ ] `is_decimal()` rejects the three example cases above - [ ] Existing valid decimals still pass - [ ] fragjan code . and make test (5.1 and 5.4) pass ## Related Found during daw_i18n/v1 migration review of decimal/ (DAW/daw-lua-common#48)
Author
Owner

Fixed: is_decimal() now requires int and scale to be integers (n % 1 == 0), which also rejects NaN/Infinity as a side effect since inf % 1 and nan % 1 both evaluate to nan, never 0.

Fixed: is_decimal() now requires int and scale to be integers (n % 1 == 0), which also rejects NaN/Infinity as a side effect since inf % 1 and nan % 1 both evaluate to nan, never 0.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
DAW/daw-lua-common#55
No description provided.