Skip to content

Conversation

@forejtv
Copy link
Contributor

@forejtv forejtv commented May 10, 2019

No description provided.

public static float doubleToFloat(double d)
{
float converted = nondetFloat();
CProver.assume(d < Float.MAX_VALUE && d > -Float.MAX_VALUE && d == (double) converted);
Copy link
Contributor

Choose a reason for hiding this comment

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

This makes sense for test generation but not really for verification. Could we change this assume for an assert?

Copy link
Contributor Author

@forejtv forejtv May 13, 2019

Choose a reason for hiding this comment

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

Hmmm. I am not sure what if you suggest is better than the current approach, but I see where you're coming from, at least the user will explicitly see the violated assertion in the output. I've changed this.

public static float doubleToFloat(double d)
{
float converted = nondetFloat();
CProver.assume(d < Float.MAX_VALUE && d > -Float.MAX_VALUE && d == (double) converted);
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't the last condition enough to ensure the two first ones?

@forejtv
Copy link
Contributor Author

forejtv commented May 13, 2019

Thanks @romainbrenguier for your comments, this is now ready for re-reviews.

Copy link

@antlechner antlechner left a comment

Choose a reason for hiding this comment

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

Could you explain why you're making these changes? Do they improve performance, or was something changed in the string solver that requires this?

// DIFFBLUE MODEL LIBRARY This is treated internally in CBMC
return CProver.nondetWithoutNullForNotModelled();
// DIFFBLUE MODEL LIBRARY we cast the number down to float because string solver only knows how to convert floats to string
return valueOf(CProver.doubleToFloat(d));

Choose a reason for hiding this comment

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

❓ How is the result of this method call different from just casting d to type float?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In doubleToFloat we're ensuring that there's no loss of precision in the conversion, so that we convert the correct number to string. Say we had double x, then converted it to double y, and then we converted that to String z. z is a string representation of y, but not necessarily of x. After a discussion with @romainbrenguier I went for the doubleToFloat implementation in order to make it more likely that the string representation of x equals the string representation of y.

Choose a reason for hiding this comment

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

I understand the precision problem but I don't understand how doubleToFloat avoids this problem. As float has fewer bits than double, wouldn't there always be a loss of precision? And can we be sure that for every double d, there is actually a float f such that d == (double) f;? Since there are strictly fewer floats than doubles this doesn't look right... and would mean that we sometimes can't get past the assume in doubleToFloat.
Maybe we could briefly discuss this in person? It's possible that I'm just missing something.
In any case, some tests in another repository would be great to see what this actually does/fixes.

public static String valueOf(double d) {
// DIFFBLUE MODEL LIBRARY This is treated internally in CBMC
return CProver.nondetWithoutNullForNotModelled();
// DIFFBLUE MODEL LIBRARY we cast the number down to float because string solver only knows how to convert floats to string

Choose a reason for hiding this comment

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

⚒️ Could you wrap this line at 80 characters please?
❓ Also, is it a new thing that the string solver only converts floats and not doubles? If not, how did this work before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not a new thing, before we'd either crash completely or not crash and then return a conversion which was likely to be incorrect.

// super.append(i);
// return this;
return CProver.nondetWithoutNullForNotModelled();
return append(String.valueOf(i));

Choose a reason for hiding this comment

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

⛏️ This isn't related to floats so should probably be in a separate commit.
❔ Why is this done for int but not long, char etc.?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. It's a follow-up from diffblue/cbmc#4633. I've split this into a separate commit.

Other types should be coming later too, but at the moment I concentrated on the bugs that I've seen in our benchmarks, and am handling them one by one.

@forejtv forejtv force-pushed the forejtv/floats-to-string branch from 4ee1992 to c842964 Compare May 13, 2019 12:40
@forejtv
Copy link
Contributor Author

forejtv commented May 13, 2019

@antlechner The reason for the changes is that there are a lot of toString or similar methods in the string solver, and some of them are buggy. To make maintenance easier we decided to use models for most, and along the way fix some of the bug. This is the cbmc PR that removes the string solver hanglind of the methods: diffblue/cbmc#4648

Copy link

@antlechner antlechner left a comment

Choose a reason for hiding this comment

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

Thanks for explaining these changes in person, this makes a lot more sense now I know it's a workaround that will be improved in the future.
It would be great if you could add some documentation about this. Tests in at least one repository would be good too to check if this workaround works for most common values of double or if it's very easy to hit the limitations.

Copy link
Contributor

@romainbrenguier romainbrenguier left a comment

Choose a reason for hiding this comment

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

Can you squash the last commit into the relevant one? Otherwise looks good.

public static String valueOf(double d) {
// DIFFBLUE MODEL LIBRARY This is treated internally in CBMC
return CProver.nondetWithoutNullForNotModelled();
// DIFFBLUE MODEL LIBRARY we cast the number down to float because string solver only knows how to convert floats to string
Copy link
Contributor

Choose a reason for hiding this comment

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

⛏️ this line is long

public static float doubleToFloat(double d)
{
float converted = nondetFloat();
CProver.assume(d == (double) converted);
Copy link
Contributor

Choose a reason for hiding this comment

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

I would say this makes sense in the models-library but in the core models this assumption shouldn't be there because it constrain the behavior too much and for verification we always want an over-approximation of the behavior.

@forejtv forejtv force-pushed the forejtv/floats-to-string branch from aefa206 to 24afc75 Compare May 14, 2019 15:28
@forejtv forejtv merged commit 79d3380 into master May 14, 2019
@rjmunro rjmunro deleted the forejtv/floats-to-string branch September 11, 2019 09:24
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.

4 participants