Restrictive casting check for imputer

### Describe the bug

Hello :)

I tried using the SimpleImputer and stumbled upon a probably undesired behaviour (at least a behaviour I wish I had the hand on).

I had a column containing only integers with an integer dtype (FYI, an integer column can't contains np.nan)) that I was trying to Impute by filling it with a constant float value. However, because of `same_kind` cast check here https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/impute/_base.py#L423-L425, this raises the following error.

```
ValueError: fill_value=0.0 (of type <class 'numpy.float32'>) cannot be cast to the input data that is dtype('int64'). If fill_value is a Python scalar, instead pass  a numpy scalar (e.g. fill_value=np.uint8(0) if your data is of type np.uint8). Make sure that both dtypes are of the same kind.
```

 Indeed, we have the following checks

```python
import numpy as np

np.can_cast(from_=np.float32, to=np.int64, casting="same_kind")
# False <= in this case, sklearn raises an error that's blocking my generic pipelines
np.can_cast(from_=np.int64, to=np.float32, casting="same_kind")
# True
np.can_cast(from_=np.float32, to=np.int64, casting="unsafe")
# True
```

which makes total sense.

Because I'm trying to write generic pipelines, I'd like to allow for unsafe type casting for the fill_value. This could be done by adding an option in the SimpleImputer `allow_fill_value_unsafe_casting` (quite verbose, but at least explicit). If True, the `casting` argument of `np.can_cast` would be `unsafe`.

### Steps/Code to Reproduce

```
import numpy as np
from sklearn.impute import SimpleImputer

X = np.array([1, 2, 3], dtype=np.int64)
imputer = SimpleImputer(strategy='constant', fill_value=np.float32(0))
imputer.fit(X.reshape(-1, 1))
```

### Expected Results

I expect this thing to work properly

### Actual Results

```
428     # print("fill_value_dtype", fill_value_dtype)
    429     # print("X.dtype", X.dtype)
    430     # Make sure we can safely cast fill_value dtype to the input data dtype
    431     if not np.can_cast(fill_value_dtype, X.dtype, casting="same_kind"):
--> [432](https://file+.vscode-resource.vscode-cdn.net/Users/datategy/Documents/papai-ml/papai-ml-1/~/Documents/papai-ml/papai-ml-1/.venv/lib/python3.10/site-packages/sklearn/impute/_base.py:432)         raise ValueError(err_msg)
    434 return X

ValueError: fill_value=0.0 (of type <class 'numpy.float32'>) cannot be cast to the input data that is dtype('int64'). If fill_value is a Python scalar, instead pass  a numpy scalar (e.g. fill_value=np.uint8(0) if your data is of type np.uint8). Make sure that both dtypes are of the same kind.
```

### Versions

```shell
System:
    python: 3.10.11 (v3.10.11:7d4cc5aa85, Apr  4 2023, 19:05:19) [Clang 13.0.0 (clang-1300.0.29.30)]
executable: /Users/datategy/Documents/papai-ml/papai-ml-1/.venv/bin/python
   machine: macOS-14.4.1-arm64-arm-64bit

Python dependencies:
      sklearn: 1.7.2
          pip: 25.3
   setuptools: 65.5.0
        numpy: 1.26.4
        scipy: 1.13.1
       Cython: None
       pandas: 1.5.3
   matplotlib: 3.8.4
       joblib: 1.5.2
threadpoolctl: 3.6.0

Built with OpenMP: True

threadpoolctl info:
       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libopenblas
       filepath: /Users/datategy/Documents/papai-ml/papai-ml-1/.venv/lib/python3.10/site-packages/numpy/.dylibs/libopenblas64_.0.dylib
        version: 0.3.23.dev
threading_layer: pthreads
   architecture: armv8

       user_api: blas
   internal_api: openblas
    num_threads: 8
         prefix: libopenblas
       filepath: /Users/datategy/Documents/papai-ml/papai-ml-1/.venv/lib/python3.10/site-packages/scipy/.dylibs/libopenblas.0.dylib
        version: 0.3.27
threading_layer: pthreads
   architecture: neoversen1

       user_api: openmp
   internal_api: openmp
    num_threads: 8
         prefix: libomp
       filepath: /Users/datategy/Documents/papai-ml/papai-ml-1/.venv/lib/python3.10/site-packages/sklearn/.dylibs/libomp.dylib
        version: None
```

COMMENT:
> But it will hinder the possibility to load the model joblib in another context since the custom transformer would not be defined there. 

It's common practice for orgs to have an internal package where they put their custom estimators, and add that package as a dependency in their runtime. So that solves the loading the persisted model issue.

> Could you enlighten me as to why this could not be done in scikit learn?

Because I'm still not convinced you don't have another way (even w/o a custom estimator) to handle this. I understand your minimal reproducer isn't representing your actual case, but as a result it's not communicating why it's really needed.

We have to strike a balance between what's inside scikit-learn, how verbose our constructor arguments are, and how many users would be affected by that. Otherwise it'd be really hard to handle feature creep in the package.

Labels: Bug, Needs Triage