Skip to content

Pin the logarithmic subtraction edge cases for Level - #1725

Open
Rafael-SOWNet wants to merge 1 commit into
angularsen:masterfrom
Rafael-SOWNet:test/level-logarithmic-edge-cases
Open

Pin the logarithmic subtraction edge cases for Level#1725
Rafael-SOWNet wants to merge 1 commit into
angularsen:masterfrom
Rafael-SOWNet:test/level-logarithmic-edge-cases

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown

Motivation

While reproducing #1569 I found that Level's logarithmic operators are exercised by
LevelTestsBase.ArithmeticOperators for the ordinary case, but not where the linear result
stops being positive. Two such cases return silently today:

Level.FromDecibels(40) - Level.FromDecibels(40)    ->  -Infinity dB
Level.FromDecibels(0.7) - Level.FromDecibels(1.6)  ->  NaN dB

The second is inconsistent with the rest of the quantity. Level(double quantity, double reference) rejects a non-positive ratio with ArgumentOutOfRangeException and the message
"The base-10 logarithm of a number ≤ 0 is undefined", while operator- performs that same
logarithm and yields NaN without signalling anything.

Changes

Three tests in UnitsNet.Tests/CustomCode/LevelTests.cs:

  • LogarithmicSubtraction_OfEqualLevels_ReturnsNegativeInfinity
  • LogarithmicSubtraction_WhenSubtrahendIsLarger_ReturnsNaN
  • LogarithmicAddition_OfTwoLevels_CombinesThemInLinearSpace, using the values from Calculations with db are wrong #1569 and
    asserting against 10 * log10(10^0.16 + 10^0.07) rather than a bare literal, so the test
    states why the answer is what it is.

What this does not do

It changes no behaviour, and none of these tests fail without a source change. They are
characterisation tests: they pin what the operators do today so that any later change to it is
a visible decision rather than a silent one. I have deliberately not made operator- throw —
that is a breaking change and your call, not something to slip in under a test PR. If you would
like it to throw, say so and I will open that separately with these tests updated.

Nothing is asserted about AmplitudeRatio or PowerRatio, which have the same logarithmic
machinery with a scaling factor of 20 and 10 respectively. I stopped at Level because that is
what #1569 is about; happy to extend if useful.

On #1569 itself

I do not think it is a bug, and I have commented there with the measurement. Level is
logarithmic, so + combines two levels in linear power space; 1.6 dB + 0.7 dB is
4.18357203248652 dB, which matches 10·log10(10^0.16 + 10^0.07) exactly. The reporter wanted
plain arithmetic on the decibel numbers, which is the right operation for a gain rather than a
level, and already works via Level.FromDecibels(a.Decibels + b.Decibels).

Validation

dotnet test UnitsNet.Tests/UnitsNet.Tests.csproj -f net10.0 --filter "FullyQualifiedName~LevelTests"
Passed!  - Failed: 0, Passed: 82, Skipped: 0, Total: 82

Local run on .NET 10 only; I have not run the full multi-target matrix.

Level's logarithmic operators are covered by LevelTestsBase for the ordinary
case, but not where the linear result stops being positive. Two such cases
return silently today:

  v - v            -> -Infinity, since log10(0) is -infinity
  smaller - larger -> NaN, since log10 of a negative number is undefined

The second is inconsistent with the rest of the quantity: the Level(quantity,
reference) constructor rejects a non-positive ratio with
ArgumentOutOfRangeException and the message "The base-10 logarithm of a number
<= 0 is undefined", while operator- performs that same logarithm and yields NaN
without signalling anything.

These tests pin the current behaviour rather than endorse it, so that any later
change to it is a visible decision.

Also adds the addition case from angularsen#1569 with the reporter's own values, asserted
against 10 * log10(10^0.16 + 10^0.07), since that issue reads the result as
wrong on the assumption that adding levels adds their decibel numbers.
@Rafael-SOWNet

Copy link
Copy Markdown
Author

CI evidence, since this PR shows no checks (first-time-contributor gate).

Full matrix run on my fork: https://github.com/Rafael-SOWNet/UnitsNet/actions/runs/31136648228success.

Passed! - Failed: 0, Passed: 52563, Skipped: 16, Total: 52579   UnitsNet.Tests
Passed! - Failed: 0, Passed:  1731, Skipped:  0, Total:  1731   UnitsNet.NumberExtensions.Tests
Passed! - Failed: 0, Passed:  1731, Skipped:  0, Total:  1731   UnitsNet.NumberExtensions.CS14.Tests
Passed! - Failed: 0, Passed:   213, Skipped:  0, Total:   213   UnitsNet.Serialization.JsonNet.Tests
Passed! - Failed: 0, Passed:   241, Skipped:  0, Total:   241   UnitsNet.Serialization.SystemTextJson.Tests

The same workflow on the same base without this PR's commit passes 52560 in UnitsNet.Tests
(https://github.com/Rafael-SOWNet/UnitsNet/actions/runs/31136388644), so the delta is exactly the
three tests added here and nothing else moved.

The run is from ci-check/level-logarithmic, which is this PR's commit plus one extra commit
touching only .github/workflows/ci.yml — that is #1727, needed because the dispatch inputs are
currently broken and dispatching is the only way to get a matrix run while checks are gated. The
diff between that branch and this PR's head is that workflow file alone; UnitsNet/ and
UnitsNet.Tests/ are byte-identical.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants