Repository: microsoft/onnxruntime
File: onnxruntime/core/providers/qnn/builder/opbuilder/batch_norm_op_builder.cc (C++)

Code before:
        raw_tensor.resize(double_tensor.size() * sizeof(MLFloat16));
        MLFloat16* raw_ptr = reinterpret_cast<MLFloat16*>(raw_tensor.data());
        for (size_t i = 0; i < double_tensor.size(); ++i) {
          raw_ptr[i] = MLFloat16(static_cast<float>(double_tensor[i]));
        }
        break;
      }
      case QNN_DATATYPE_UFIXED_POINT_32:
      case QNN_DATATYPE_UFIXED_POINT_16:
      case QNN_DATATYPE_UFIXED_POINT_8:
      case QNN_DATATYPE_SFIXED_POINT_32:
      case QNN_DATATYPE_SFIXED_POINT_16:
      case QNN_DATATYPE_SFIXED_POINT_8:
      case QNN_DATATYPE_BOOL_8:
      case QNN_DATATYPE_STRING:
      default:
        return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Qnn Data Type: ", qnn_data_type, " is not supported yet.");
    }
    return Status::OK();
  }

  // Maybe de-quantizes a 1D BatchNorm parameter tensor to double values.
  Status MaybeDequantizeParamTensor(const TensorInfo& info,
                                    const uint8_t* raw_ptr,
                                    const size_t raw_ptr_length,
                                    const char* tensor_name,
                                    std::vector<double>& out) const {
    uint32_t channel = info.shape[0];
    out.resize(channel);
    ORT_RETURN_IF_ERROR(AssertUnpackedTensorSize(info.qnn_data_type, channel, raw_ptr_length));

    const bool is_quantized = info.quant_param.IsQuantized();
    const bool is_per_channel = info.quant_param.IsPerChannel();
    const Qnn_QuantizeParams_t& quant_param = info.quant_param.Get();
    if (is_per_channel) {
      // Validate per-channel quantization parameters for 1D BatchNorm tensors.
      // For 1D tensors, axis must be 0 and numScaleOffsets must be >= channel count.
      ORT_RETURN_IF_NOT(quant_param.axisScaleOffsetEncoding.axis == 0,
                        "Per-channel quantization axis must be 0 for 1D ", tensor_name, " tensor, got ",
                        quant_param.axisScaleOffsetEncoding.axis);
      ORT_RETURN_IF_NOT(quant_param.axisScaleOffsetEncoding.numScaleOffsets >= channel,
                        "Per-channel quantization scale/offset count (",
                        quant_param.axisScaleOffsetEncoding.numScaleOffsets,
                        ") is less than channel count (", channel, ") for ", tensor_name, " tensor.");
    }

    int offset = 0;
    for (uint32_t i = 0; i < channel; ++i) {
      double value = 0.0;
      ORT_RETURN_IF_ERROR(GetValueOnQnnDataType(info.qnn_data_type, raw_ptr + offset, value, offset));
      // Dequantize if needed

Review comment (bug):
>const char* tensor_name [](http://example.com/codeflow?start=36&length=23)

POinters denote optional params and should be checked for nullptr. Otherwise, pass a reference to std::string or std::string_view #Resolved

Code after:
        raw_tensor.resize(double_tensor.size() * sizeof(MLFloat16));
        MLFloat16* raw_ptr = reinterpret_cast<MLFloat16*>(raw_tensor.data());
        for (size_t i = 0; i < double_tensor.size(); ++i) {
          raw_ptr[i] = MLFloat16(static_cast<float>(double_tensor[i]));
        }
        break;
      }
      case QNN_DATATYPE_UFIXED_POINT_32:
      case QNN_DATATYPE_UFIXED_POINT_16:
      case QNN_DATATYPE_UFIXED_POINT_8:
      case QNN_DATATYPE_SFIXED_POINT_32:
      case QNN_DATATYPE_SFIXED_POINT_16:
      case QNN_DATATYPE_SFIXED_POINT_8:
      case QNN_DATATYPE_BOOL_8:
      case QNN_DATATYPE_STRING:
      default:
        return ORT_MAKE_STATUS(ONNXRUNTIME, FAIL, "Qnn Data Type: ", qnn_data_type, " is not supported yet.");
    }
    return Status::OK();
  }

  // Maybe dequantizes a 1D BatchNorm parameter tensor to double values.
  Status MaybeDequantizeParamTensor(const TensorInfo& info,
                                    const uint8_t* raw_ptr,
                                    const size_t raw_ptr_length,
                                    std::string_view tensor_name,
                                    std::vector<double>& out) const {
    uint32_t channel = info.shape[0];
    out.resize(channel);
    ORT_RETURN_IF_ERROR(AssertUnpackedTensorSize(info.qnn_data_type, channel, raw_ptr_length));

    const bool is_quantized = info.quant_param.IsQuantized();
    const bool is_per_channel = info.quant_param.IsPerChannel();
    const Qnn_QuantizeParams_t& quant_param = info.quant_param.Get();
    if (is_per_channel) {
      // Validate per-channel quantization parameters for 1D BatchNorm tensors.
      // For 1D tensors, axis must be 0 and numScaleOffsets must equal channel count.
      ORT_RETURN_IF_NOT(quant_param.axisScaleOffsetEncoding.axis == 0,
                        "Per-channel quantization axis must be 0 for 1D ", tensor_name, " tensor, got ",
                        quant_param.axisScaleOffsetEncoding.axis);
      ORT_RETURN_IF_NOT(quant_param.axisScaleOffsetEncoding.numScaleOffsets == channel,
                        "Per-channel quantization scale/offset count (",
                        quant_param.axisScaleOffsetEncoding.numScaleOffsets,
                        ") must equal channel count (", channel, ") for ", tensor_name, " tensor.");
    }

    int offset = 0;
    for (uint32_t i = 0; i < channel; ++i) {
      double value = 0.0;
      ORT_RETURN_IF_ERROR(GetValueOnQnnDataType(info.qnn_data_type, raw_ptr + offset, value, offset));
      // Dequantize if needed