【问题标题】:XCTAssertEqualObjects comparing NSArrays of NSNumber fails even though arrays appear identicalXCTAssertEqualObjects 比较 NSNumber 的 NSArrays 失败,即使数组看起来相同
【发布时间】:2016-10-31 10:53:03
【问题描述】:

我正在用 Objective-C 开发一个人工神经网络,所以我写了一些矩阵向量算术的方法。例如,下面是外积计算的代码。代码工作正常并返回所需的结果,但是在将方法返回的NSMutableArray 对象与单元测试中创建的对象进行比较时,我的单元测试失败了。我已经迷失了几天了。有谁知道为什么XCTAssertEqualObjects() 会失败,尽管对象看起来相同?

这是在 MLNNeuralNet.m 中返回 2 个向量(NSArrays)的外积的相关代码:

-(NSMutableArray *)outerProduct:(NSArray *)matrix1 by:(NSArray *)matrix2 {

/*Tensor Product of 2 vectors treated as column and row matrices, respectively*/

/*Example: if matrix1 is @[2, 4, 6] and matrix2 @[3, 4, 5], then calculation is:
 [2 * 3, 2 * 4, 2 * 5], [4 * 3, etc...]
 and result is:
 @[@[6, 8, 10], @[12, 16, 20], @[18, 24, 30]]
 */

NSMutableArray *result = [[NSMutableArray alloc] init];

for (int i = 0; i < [matrix1 count]; i++) {
    NSMutableArray *tempArray = [[NSMutableArray alloc] init];
    for (int j = 0; j < [matrix2 count]; j++) {
        double product = [[matrix1 objectAtIndex:i] doubleValue] * [[matrix2 objectAtIndex:j] doubleValue];
        [tempArray addObject:@(product)];
    }
    [result addObject:tempArray];
}

return result;
}

这是单元测试的代码:

@interface MLNNeuralNetTests : XCTestCase

@property (strong, nonatomic) MLNNeuralNet *neuralNet;

@end

@implementation MLNNeuralNetTests

- (void)setUp {
    [super setUp];
    _neuralNet = [[MLNNeuralNet alloc] init];
}

-(void)testOuterProduct {

NSMutableArray *matrix1 = [[NSMutableArray alloc] initWithArray:@[@(1.0), @(2.0), @(3.0)]];
NSMutableArray *matrix2 = [[NSMutableArray alloc] initWithArray:@[@(4.2), @(5.2), @(6.2)]];

NSMutableArray *layer1 = [[NSMutableArray alloc] initWithArray:@[@(4.2), @(5.2), @(6.2)]];
NSMutableArray *layer2 = [[NSMutableArray alloc] initWithArray:@[@(8.4), @(10.4), @(12.4)]];
NSMutableArray *layer3 = [[NSMutableArray alloc] initWithArray:@[@(12.6), @(15.6), @(18.6)]];
NSMutableArray *correctMatrix = [[NSMutableArray alloc]
                                 initWithArray:@[layer1, layer2, layer3]];

NSMutableArray *testMatrix = [self.neuralNet outerProduct:matrix1 by:matrix2];

XCTAssertEqualObjects(correctMatrix, testMatrix, @"Matrix outer product failed");
}

这是我得到的错误:

我认为这可能是由于我在单元测试版本中创建了 NSNumber 文字,例如 @(4.2) etc...

所以我尝试先创建doubles,然后像这样包装NSNumber

double number1 = 4.2;
NSMutableArray *layer1 = [[NSMutableArray alloc] initWithArray:@[@(number1), etc...

但这也没有用。

我错过了什么吗?

当我尝试在类似测试中测试对象相等性时,我没有遇到任何问题。例如,以下测试不会失败:

-(void)testMultiplyVectorElements {

    NSArray *vector1 = @[@(1.0), @(2.0), @(3.0), @(4.0)];
    NSArray *vector2 = @[@(5.2), @(6.2), @(7.2), @(8.2)];
    NSMutableArray *correctVector = [[NSMutableArray alloc] initWithArray:@[@(5.2), @(12.4), @(21.6), @(32.8)]];
    NSMutableArray *testVector = [self.neuralNet multiplyVectorElements:vector1 by:vector2];

    XCTAssertEqualObjects(correctVector, testVector, @"Vector element-wise multiplication failed.");
}

【问题讨论】:

    标签: objective-c cocoa-touch nsmutablearray xctest nsnumber


    【解决方案1】:

    我相信这取决于浮点运算。浮点比较可能很棘手。如果这些是“真实”数字,组合它们的结果不会完全产生您所期望的结果。

    XCTAssertEqualObjects() 的输出使用NSLog() 打印NSNumbers,将它们四舍五入以便显示。您可以手动检查并查看更精确的值:

    NSUInteger row_idx = 0;
    for( NSArray<NSNumber *> * row in testMatrix ){
        NSUInteger col_idx = 0;
        for( NSNumber * testProduct in row ){
            NSNumber * correctProduct = correctRow[row_idx][col_idx];
            NSLog(@"%.16lf %.16lf", [product doubleValue], correctProduct);
            /* This level of accuracy fails with your code. Drop at
             * least one 0 to pass the assertion.
             */
            XCTAssertEqualWithAccuracy([product doubleValue],
                                       [correctProduct doubleValue],
                                       0.000000000000001);
            col_number += 1;
        }
        row_number += 1;
    }
    

    由此可以看出,乘法应该得到的 12.6 实际上是 12.6000000000000014,而 correctMatrix 12.6 中的字面量存储为 12.5999999999999996。所以他们非常接近,但不是==

    XCTAssertEqualWithAccuracy() 宏是为比较浮点值而设计的。它允许您传递第三个值来创建一个范围,在该范围内这些值被认为是“足够相等”。

    如果您要进行大量数值 Cocoa 计算,另一种选择是切换到 NSDecimalNumber,它确实提供了精确的“真实”值进行算术运算。权衡是它比NSNumber 更让人头疼,因为所有操作都经过方法。 [x decimalNumberByMultiplyingBy:y].

    【讨论】:

    • 感谢您的回答,我为此苦苦挣扎。
    猜你喜欢
    • 2021-11-13
    • 2013-04-19
    • 2016-12-05
    • 1970-01-01
    • 2016-08-05
    • 2020-04-03
    • 1970-01-01
    • 2014-01-28
    • 1970-01-01
    相关资源
    最近更新 更多