Skip to content

Conversation

@tautschnig
Copy link
Collaborator

Handle special values (0, ±π/2, ±π, ±3π/2, ±2π) with precise ranges and and use quadrant-based range narrowing to reduce over-approximation.

Fixes: #6999

  • Each commit message has a non-empty body, explaining why the change was made.
  • n/a Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
  • n/a The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/
  • Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).
  • n/a My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • n/a White-space or formatting changes outside the feature-related changed lines are in commits of their own.

Handle special values (0, ±π/2, ±π, ±3π/2, ±2π) with precise ranges and
and use quadrant-based range narrowing to reduce over-approximation.

Fixes: diffblue#6999

// cos(0) should be exactly 1
c = cos(0.0);
assert(c >= 0.999999999 && c <= 1.0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compare with 1, as suggested in the comment.


// cos(0) should be exactly 1
c = cos(0.0);
assert(c >= 0.999999999 && c <= 1.0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.999999999 == 0x1.fffffff768fa1p-1

so this allows around 22 bits difference so only a bit more accurate than float. I would recommend a tighter bound.

c = cos(0.0);
assert(c >= 0.999999999 && c <= 1.0);

// cos(π/2) should be approximately 0
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can work it out exactly. If we use a correctly rounded implementation such as https://core-math.gitlabpages.inria.fr/ then we can compute the double nearest to cos(M_PI / 2.0). Note that it is small but it is not zero. In fact, IIRC, it is a lot further away from zero than you might think because M_PI != \pi.

Personally I would try computing each of these using something like CORE-MATH and then adding an error in the range of a few ULP. Happy to give this a go if useful.


// ========== COSINE TESTS ==========

// Test 7: cos(0) ≈ 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These just repeat the above.

// Test 13: sinf with special values
{
float s0 = sinf(0.0f);
float s_pi_half = sinf(3.14159265f / 2.0f);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File is called cosf, but tests sinf.

^VERIFICATION SUCCESSFUL$
--
^warning: ignoring
^warning: ignoring
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spurious newline change


// Test range narrowing for [-π/2, π/2]
c = cos(M_PI / 4.0); // 45 degrees
assert(c >= 0.0 && c <= 1.0); // Should be in [0, 1]
Copy link
Collaborator

@martin-cs martin-cs Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike sin, which can produce very small outputs (but only in [-\pi/2, \pi/2], cos has quite a high lower bound on it's outputs. Off the top of my head I think it is something like 0x1.0p-100, very small but a tiny part of the range of double.

^SIGNAL=0$
^VERIFICATION SUCCESSFUL$
--
^warning: ignoring No newline at end of file
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spurious newline change

^VERIFICATION SUCCESSFUL$
--
^warning: ignoring
^warning: ignoring
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spurious newline change

do
// sin(0) should be exactly 0
s = sin(0.0);
assert(s >= -1e-10 && s <= 1e-10);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above


// ========== COSINE TESTS ==========

// Test 7: cos(0) ≈ 1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a duplicate of line 10.

double x;
__CPROVER_assume(x >= -M_PI / 2.0 + 0.1 && x <= M_PI / 2.0 - 0.1);
double c = cos(x);
assert(c >= 0.0 && c <= 1.0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, these can be tightened quite a bit.


// Handle exact special values
// sin(0) = 0
if(x >= -epsilon && x <= epsilon)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would handle the special case 0 in addition to that.

Copy link
Collaborator

@martin-cs martin-cs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some pedantry. Hope it is helpful.

const long double two_pi = 6.28318530717958647692L;

// For small epsilon for floating-point comparisons
const long double epsilon = 1e-12L;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this number?


// For small epsilon for floating-point comparisons
const long double epsilon = 1e-12L;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that it would be possible to do a range reduction here and it might get you more accurate results.

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.

Models for sine and cosine builtins should be more precise

3 participants