Skip to content

Instantly share code, notes, and snippets.

@mikecsh
Created January 23, 2013 11:19
Show Gist options
  • Save mikecsh/4604538 to your computer and use it in GitHub Desktop.
Save mikecsh/4604538 to your computer and use it in GitHub Desktop.
CPDecimalCompare Problem
/*
* AppController.j
* CPDecimalTest
*
* Created by You on January 11, 2013.
* Copyright 2013, Your Company All rights reserved.
*/
var debugCPDecimalCompare;
@import <Foundation/CPObject.j>
@implementation AppController : CPObject
{
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask],
contentView = [theWindow contentView];
var label = [[CPTextField alloc] initWithFrame:CGRectMakeZero()];
[label setStringValue:@"Open the console!"];
[label setFont:[CPFont boldSystemFontOfSize:24.0]];
[label sizeToFit];
[label setAutoresizingMask:CPViewMinXMargin | CPViewMaxXMargin | CPViewMinYMargin | CPViewMaxYMargin];
[label setCenter:[contentView center]];
[contentView addSubview:label];
[theWindow orderFront:self];
debugCPDecimalCompare = NO;
console.log("The following tests show that [CPDecimalNumber -compare:] incorrectly evaluates numbers in the range 0 to 0.999999... as less than zero.");
[self isNegative:@"1"];
[self isNegative:@"0.99999"];
[self isNegative:@"0.25"];
[self isNegative:@"0.0"];
[self isNegative:@"0"];
[self isNegative:@"0.25"];
[self isNegative:@"2.25"];
[self isNegative:@"-2.25"];
console.log("\nEnabling debug flag to log CPDecimalCompare internals");
debugCPDecimalCompare = YES;
console.log("\nThe following is WRONG");
[self isNegative:@"0.25"];
console.log("\nThe following is CORRECT");
[self isNegative:@"1.25"];
console.log("\nFor some reason the rightOperand._mantissa.length of [CPDecimalNumber zero] is different over the last two calls..");
}
- (void)isNegative:(CPString)stringRepresentationOfNumber
{
var decimalNumber = [CPDecimalNumber decimalNumberWithString:stringRepresentationOfNumber],
comparisonResult = [decimalNumber compare:[CPDecimalNumber zero]];
console.log("\nEvaluating: is " + [decimalNumber stringValue] + " < 0?");
if (comparisonResult === CPOrderedAscending)
{
console.log("\tCappuccino thinks " + stringRepresentationOfNumber + " is less than zero");
}
else if (comparisonResult === CPOrderedSame)
{
console.log("\tCappuccino thinks " + stringRepresentationOfNumber + " is equal to zero");
}
else if (comparisonResult === CPOrderedDescending)
{
console.log("\tCappuccino thinks " + stringRepresentationOfNumber + " is greater thank zero");
}
}
@end
/*!
* This function has been taken from CPDecimal.j and had some logging added to try and work out what's going on.
*/
function CPDecimalCompare(leftOperand, rightOperand)
{
if (leftOperand._isNaN && rightOperand._isNaN)
return CPOrderedSame;
if (leftOperand._isNegative != rightOperand._isNegative)
{
if (rightOperand._isNegative)
return CPOrderedDescending;
else
return CPOrderedAscending;
}
// Before comparing number size check if zero (dont use CPDecimalIsZero as it is more computationally intensive)
var leftIsZero = (leftOperand._mantissa.length == 1 && leftOperand._mantissa[0] == 0),
rightIsZero = (rightOperand._mantissa.length == 1 && rightOperand._mantissa[0] == 0),
// Sign is the same, quick check size (length + exp)
s1 = leftOperand._exponent + leftOperand._mantissa.length,
s2 = rightOperand._exponent + rightOperand._mantissa.length;
/*!
* The problem *seems* to be that s1 < s2 for numbers >= 0 and < 1, and rightOperand._isNegative is (correctly) false
* This means
*/
if (leftIsZero || s1 < s2)
{
if (debugCPDecimalCompare)
{
console.log("leftOperand:", CPDecimalString(leftOperand, nil));
console.log("rightOperand:", CPDecimalString(rightOperand, nil));
console.log("leftIsZero:", leftIsZero ? "true" : "false");
console.log("rightIsZero:", rightIsZero ? "true" : "false");
console.log("s1 < s2:", s1 < s2 ? "true" : "false");
console.log("leftOperand._exponent:", leftOperand._exponent);
console.log("leftOperand._mantissa.length:", leftOperand._mantissa.length);
console.log("rightOperand._exponent:", rightOperand._exponent);
console.log("rightOperand._mantissa.length:", leftOperand._mantissa.length);
}
if (rightOperand._isNegative)
{
if (debugCPDecimalCompare)
console.log("rightOperand._isNegative: true");
return CPOrderedDescending;
}
else
{
if (debugCPDecimalCompare)
console.log("rightOperand._isNegative: false");
return CPOrderedAscending;
}
}
if (rightIsZero || s1 > s2)
{
if (debugCPDecimalCompare)
{
console.log("leftOperand:", CPDecimalString(leftOperand, nil));
console.log("rightOperand:", CPDecimalString(rightOperand, nil));
console.log("leftIsZero:", leftIsZero ? "true" : "false");
console.log("rightIsZero:", rightIsZero ? "true" : "false");
console.log("s1 < s2:", s1 < s2 ? "true" : "false");
console.log("leftOperand._exponent:", leftOperand._exponent);
console.log("leftOperand._mantissa.length:", leftOperand._mantissa.length);
console.log("rightOperand._exponent:", rightOperand._exponent);
console.log("rightOperand._mantissa.length:", leftOperand._mantissa.length);
}
if (debugCPDecimalCompare)
console.log("rightIsZero");
if (rightOperand._isNegative)
{
if (debugCPDecimalCompare)
console.log("rightOperand._isNegative: true");
return CPOrderedAscending;
}
else
{
if (debugCPDecimalCompare)
console.log("rightOperand._isNegative: false");
return CPOrderedDescending;
}
}
// Same size, so check mantissa
var l = MIN(leftOperand._mantissa.length, rightOperand._mantissa.length),
i = 0;
for (; i < l; i++)
{
var d = rightOperand._mantissa[i] - leftOperand._mantissa[i];
if (d > 0)
{
if (rightOperand._isNegative)
return CPOrderedDescending;
else
return CPOrderedAscending;
}
if (d < 0)
{
if (rightOperand._isNegative)
return CPOrderedAscending;
else
return CPOrderedDescending;
}
}
// Same digits, check length
if (leftOperand._mantissa.length > rightOperand._mantissa.length)
{
if (rightOperand._isNegative)
return CPOrderedAscending;
else
return CPOrderedDescending;
}
if (leftOperand._mantissa.length < rightOperand._mantissa.length)
{
if (rightOperand._isNegative)
return CPOrderedDescending;
else
return CPOrderedAscending;
}
return CPOrderedSame;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment